From 0bd26b74b26141cf4e6007a1c426461e78c41589 Mon Sep 17 00:00:00 2001 From: cdrolle Date: Mon, 25 Apr 2016 15:38:02 -0700 Subject: [PATCH] Remove Eia608TrackRenderer Functionality is moved into Eia608Parser, so that Eia608Parser can be used with TextTrackRenderer, similar to all the other text/subtitle formats. Modified some of the other text/subtitle-related classes to support the new behaviour. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=120755145 --- .../ext/vp9/LibvpxVideoTrackRenderer.java | 1 - .../text/SimpleSubtitleOutputBuffer.java | 35 ++ ...eParser.java => SimpleSubtitleParser.java} | 16 +- .../exoplayer/text/SubtitleInputBuffer.java | 14 +- .../exoplayer/text/SubtitleOutputBuffer.java | 13 +- .../exoplayer/text/SubtitleParserFactory.java | 27 +- .../exoplayer/text/TextTrackRenderer.java | 16 +- .../text/eia608/ClosedCaptionList.java | 15 +- .../exoplayer/text/eia608/Eia608Parser.java | 305 +++++++++++++-- .../exoplayer/text/eia608/Eia608Subtitle.java | 59 +++ .../eia608/Eia608SubtitleOutputBuffer.java | 38 ++ .../text/eia608/Eia608TrackRenderer.java | 351 ------------------ .../exoplayer/text/subrip/SubripParser.java | 4 +- .../exoplayer/text/ttml/TtmlParser.java | 4 +- .../exoplayer/text/tx3g/Tx3gParser.java | 6 +- .../text/webvtt/Mp4WebvttParser.java | 6 +- .../exoplayer/text/webvtt/WebvttParser.java | 4 +- .../extensions/AudioDecoderTrackRenderer.java | 1 - .../util/extensions/SimpleDecoder.java | 16 +- 19 files changed, 499 insertions(+), 432 deletions(-) create mode 100644 library/src/main/java/com/google/android/exoplayer/text/SimpleSubtitleOutputBuffer.java rename library/src/main/java/com/google/android/exoplayer/text/{SubtitleParser.java => SimpleSubtitleParser.java} (79%) create mode 100644 library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608Subtitle.java create mode 100644 library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608SubtitleOutputBuffer.java delete mode 100644 library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java index 46f3e81530..bbc31b38f0 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java @@ -208,7 +208,6 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { long startElapsedRealtimeMs = SystemClock.elapsedRealtime(); decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE); decoder.setOutputMode(outputMode); - decoder.start(); notifyDecoderInitialized(startElapsedRealtimeMs, SystemClock.elapsedRealtime()); codecCounters.codecInitCount++; } diff --git a/library/src/main/java/com/google/android/exoplayer/text/SimpleSubtitleOutputBuffer.java b/library/src/main/java/com/google/android/exoplayer/text/SimpleSubtitleOutputBuffer.java new file mode 100644 index 0000000000..c8ab69a1bc --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/text/SimpleSubtitleOutputBuffer.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer.text; + +/** + * A {@link Subtitle} output from a subtitle parser that extends {@link SimpleSubtitleParser}. + */ +public final class SimpleSubtitleOutputBuffer extends SubtitleOutputBuffer { + + private SimpleSubtitleParser owner; + + public SimpleSubtitleOutputBuffer(SimpleSubtitleParser owner) { + super(); + this.owner = owner; + } + + @Override + public final void release() { + owner.releaseOutputBuffer(this); + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/text/SubtitleParser.java b/library/src/main/java/com/google/android/exoplayer/text/SimpleSubtitleParser.java similarity index 79% rename from library/src/main/java/com/google/android/exoplayer/text/SubtitleParser.java rename to library/src/main/java/com/google/android/exoplayer/text/SimpleSubtitleParser.java index f46698bc3d..b36a05c54a 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/SubtitleParser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/SimpleSubtitleParser.java @@ -19,12 +19,12 @@ import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.util.extensions.SimpleDecoder; /** - * Parses {@link Subtitle}s from {@link SubtitleInputBuffer}s. + * Base class for subtitle parsers that use their own decode thread. */ -public abstract class SubtitleParser extends +public abstract class SimpleSubtitleParser extends SimpleDecoder { - protected SubtitleParser() { + protected SimpleSubtitleParser() { super(new SubtitleInputBuffer[2], new SubtitleOutputBuffer[2]); setInitialInputBufferSize(1024); } @@ -36,7 +36,7 @@ public abstract class SubtitleParser extends @Override protected final SubtitleOutputBuffer createOutputBuffer() { - return new SubtitleOutputBuffer(this); + return new SimpleSubtitleOutputBuffer(this); } @Override @@ -56,6 +56,14 @@ public abstract class SubtitleParser extends } } + /** + * Decodes the data and converts it into a {@link Subtitle}. + * + * @param data The data to be decoded. + * @param size The size of the data. + * @return A {@link Subtitle} to rendered. + * @throws ParserException A parsing exception. + */ protected abstract Subtitle decode(byte[] data, int size) throws ParserException; } diff --git a/library/src/main/java/com/google/android/exoplayer/text/SubtitleInputBuffer.java b/library/src/main/java/com/google/android/exoplayer/text/SubtitleInputBuffer.java index 067663517f..2c7818dc5a 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/SubtitleInputBuffer.java +++ b/library/src/main/java/com/google/android/exoplayer/text/SubtitleInputBuffer.java @@ -18,9 +18,10 @@ package com.google.android.exoplayer.text; import com.google.android.exoplayer.DecoderInputBuffer; /** - * An input buffer for {@link SubtitleParser}. + * An input buffer for a subtitle parser. */ -/* package */ final class SubtitleInputBuffer extends DecoderInputBuffer { +public final class SubtitleInputBuffer extends DecoderInputBuffer + implements Comparable { public long subsampleOffsetUs; @@ -28,4 +29,13 @@ import com.google.android.exoplayer.DecoderInputBuffer; super(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); } + @Override + public int compareTo(SubtitleInputBuffer other) { + long delta = timeUs - other.timeUs; + if (delta == 0) { + return 0; + } + return delta > 0 ? 1 : -1; + } + } diff --git a/library/src/main/java/com/google/android/exoplayer/text/SubtitleOutputBuffer.java b/library/src/main/java/com/google/android/exoplayer/text/SubtitleOutputBuffer.java index f083dcb7c8..211b6889d6 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/SubtitleOutputBuffer.java +++ b/library/src/main/java/com/google/android/exoplayer/text/SubtitleOutputBuffer.java @@ -21,17 +21,14 @@ import com.google.android.exoplayer.util.extensions.OutputBuffer; import java.util.List; /** - * A {@link Subtitle} output from a {@link SubtitleParser}. + * Base class for a {@link Subtitle} output from a subtitle parser. */ -/* package */ final class SubtitleOutputBuffer extends OutputBuffer implements Subtitle { - - private final SubtitleParser owner; +public abstract class SubtitleOutputBuffer extends OutputBuffer implements Subtitle { private Subtitle subtitle; private long offsetUs; - public SubtitleOutputBuffer(SubtitleParser owner) { - this.owner = owner; + public SubtitleOutputBuffer() { } public void setOutput(long timestampUs, Subtitle subtitle, long subsampleOffsetUs) { @@ -62,9 +59,7 @@ import java.util.List; } @Override - public void release() { - owner.releaseOutputBuffer(this); - } + public abstract void release(); @Override public void clear() { diff --git a/library/src/main/java/com/google/android/exoplayer/text/SubtitleParserFactory.java b/library/src/main/java/com/google/android/exoplayer/text/SubtitleParserFactory.java index d0c46222e6..2eb71badab 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/SubtitleParserFactory.java +++ b/library/src/main/java/com/google/android/exoplayer/text/SubtitleParserFactory.java @@ -16,30 +16,37 @@ package com.google.android.exoplayer.text; import com.google.android.exoplayer.Format; +import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.util.MimeTypes; +import com.google.android.exoplayer.util.extensions.Decoder; /** - * A factory for {@link SubtitleParser} instances. + * A factory for {@link Decoder} + * instances that will parse subtitles. */ public interface SubtitleParserFactory { /** - * Returns whether the factory is able to instantiate a {@link SubtitleParser} for the given + * Returns whether the factory is able to instantiate a + * {@link Decoder} for the given * {@link Format}. * * @param format The {@link Format}. - * @return True if the factory can instantiate a suitable {@link SubtitleParser}. False otherwise. + * @return True if the factory can instantiate a suitable + * {@link Decoder}. False + * otherwise. */ boolean supportsFormat(Format format); /** - * Creates a {@link SubtitleParser} for the given {@link Format}. + * Creates a {@link Decoder} for + * the given {@link Format}. * * @param format The {@link Format}. - * @return A new {@link SubtitleParser}. + * @return A new {@link Decoder}. * @throws IllegalArgumentException If the {@link Format} is not supported. */ - SubtitleParser createParser(Format format); + Decoder createParser(Format format); /** * Default {@link SubtitleParserFactory} implementation. @@ -51,6 +58,7 @@ public interface SubtitleParserFactory { *
  • TTML ({@link com.google.android.exoplayer.text.ttml.TtmlParser})
  • *
  • SubRip ({@link com.google.android.exoplayer.text.subrip.SubripParser})
  • *
  • TX3G ({@link com.google.android.exoplayer.text.tx3g.Tx3gParser})
  • + *
  • Eia608 ({@link com.google.android.exoplayer.text.eia608.Eia608Parser})
  • * */ SubtitleParserFactory DEFAULT = new SubtitleParserFactory() { @@ -61,13 +69,14 @@ public interface SubtitleParserFactory { } @Override - public SubtitleParser createParser(Format format) { + public Decoder createParser( + Format format) { try { Class clazz = getParserClass(format.sampleMimeType); if (clazz == null) { throw new IllegalArgumentException("Attempted to create parser for unsupported format"); } - return clazz.asSubclass(SubtitleParser.class).newInstance(); + return clazz.asSubclass(Decoder.class).newInstance(); } catch (Exception e) { throw new IllegalStateException("Unexpected error instantiating parser", e); } @@ -86,6 +95,8 @@ public interface SubtitleParserFactory { return Class.forName("com.google.android.exoplayer.text.subrip.SubripParser"); case MimeTypes.APPLICATION_TX3G: return Class.forName("com.google.android.exoplayer.text.tx3g.Tx3gParser"); + case MimeTypes.APPLICATION_EIA608: + return Class.forName("com.google.android.exoplayer.text.eia608.Eia608Parser"); default: return null; } diff --git a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java index fc8f197de6..7d7d22769d 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java @@ -24,6 +24,7 @@ import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.MimeTypes; +import com.google.android.exoplayer.util.extensions.Decoder; import android.annotation.TargetApi; import android.os.Handler; @@ -38,8 +39,9 @@ import java.util.List; /** * A {@link TrackRenderer} for subtitles. *

    - * Text is parsed from sample data using {@link SubtitleParser} instances obtained from a - * {@link SubtitleParserFactory}. The actual rendering of each line of text is delegated to a + * Text is parsed from sample data using + * {@link Decoder} instances obtained + * from a {@link SubtitleParserFactory}. The actual rendering of each line of text is delegated to a * {@link TextRenderer}. */ @TargetApi(16) @@ -54,7 +56,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback { private boolean inputStreamEnded; private boolean outputStreamEnded; - private SubtitleParser parser; + private Decoder parser; private SubtitleInputBuffer nextInputBuffer; private SubtitleOutputBuffer subtitle; private SubtitleOutputBuffer nextSubtitle; @@ -79,7 +81,8 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback { * normally be the looper associated with the application's main thread, which can be obtained * using {@link android.app.Activity#getMainLooper()}. Null may be passed if the renderer * should be invoked directly on the player's internal rendering thread. - * @param parserFactory A factory from which to obtain {@link SubtitleParser} instances. + * @param parserFactory A factory from which to obtain + * {@link Decoder} instances. */ public TextTrackRenderer(TextRenderer textRenderer, Looper textRendererLooper, SubtitleParserFactory parserFactory) { @@ -101,7 +104,6 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback { protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { super.onEnabled(formats, joining); parser = parserFactory.createParser(formats[0]); - parser.start(); } @Override @@ -174,7 +176,7 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback { } try { - if (!inputStreamEnded && nextSubtitle == null) { + while (!inputStreamEnded) { if (nextInputBuffer == null) { nextInputBuffer = parser.dequeueInputBuffer(); if (nextInputBuffer == null) { @@ -193,6 +195,8 @@ public final class TextTrackRenderer extends TrackRenderer implements Callback { } parser.queueInputBuffer(nextInputBuffer); nextInputBuffer = null; + } else if (result == TrackStream.NOTHING_READ) { + break; } } } catch (ParserException e) { diff --git a/library/src/main/java/com/google/android/exoplayer/text/eia608/ClosedCaptionList.java b/library/src/main/java/com/google/android/exoplayer/text/eia608/ClosedCaptionList.java index f47ec1f466..9e9cb84d30 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/eia608/ClosedCaptionList.java +++ b/library/src/main/java/com/google/android/exoplayer/text/eia608/ClosedCaptionList.java @@ -15,25 +15,14 @@ */ package com.google.android.exoplayer.text.eia608; -/* package */ final class ClosedCaptionList implements Comparable { +/* package */ final class ClosedCaptionList { public final long timeUs; - public final boolean decodeOnly; public final ClosedCaption[] captions; - public ClosedCaptionList(long timeUs, boolean decodeOnly, ClosedCaption[] captions) { + public ClosedCaptionList(long timeUs, ClosedCaption[] captions) { this.timeUs = timeUs; - this.decodeOnly = decodeOnly; this.captions = captions; } - @Override - public int compareTo(ClosedCaptionList other) { - long delta = timeUs - other.timeUs; - if (delta == 0) { - return 0; - } - return delta > 0 ? 1 : -1; - } - } diff --git a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608Parser.java b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608Parser.java index aed6be7ff8..de6e662667 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608Parser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608Parser.java @@ -15,18 +15,28 @@ */ package com.google.android.exoplayer.text.eia608; -import com.google.android.exoplayer.DecoderInputBuffer; -import com.google.android.exoplayer.util.MimeTypes; +import com.google.android.exoplayer.C; +import com.google.android.exoplayer.ParserException; +import com.google.android.exoplayer.text.SubtitleInputBuffer; +import com.google.android.exoplayer.text.SubtitleOutputBuffer; +import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.ParsableBitArray; import com.google.android.exoplayer.util.ParsableByteArray; +import com.google.android.exoplayer.util.extensions.Decoder; import java.util.ArrayList; +import java.util.LinkedList; +import java.util.TreeSet; /** * Facilitates the extraction and parsing of EIA-608 (a.k.a. "line 21 captions" and "CEA-608") * Closed Captions from the SEI data block from H.264. */ -public final class Eia608Parser { +public final class Eia608Parser implements + Decoder { + + private static final int NUM_INPUT_BUFFERS = 10; + private static final int NUM_OUTPUT_BUFFERS = 2; private static final int PAYLOAD_TYPE_CC = 4; private static final int COUNTRY_CODE = 0xB5; @@ -34,6 +44,14 @@ public final class Eia608Parser { private static final int USER_ID = 0x47413934; // "GA94" private static final int USER_DATA_TYPE_CODE = 0x3; + private static final int CC_MODE_UNKNOWN = 0; + private static final int CC_MODE_ROLL_UP = 1; + private static final int CC_MODE_POP_ON = 2; + private static final int CC_MODE_PAINT_ON = 3; + + // The default number of rows to display in roll-up captions mode. + private static final int DEFAULT_CAPTIONS_ROW_COUNT = 4; + // Basic North American 608 CC char set, mostly ASCII. Indexed by (char-0x20). private static final int[] BASIC_CHARACTER_SET = new int[] { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // ! " # $ % & ' @@ -102,28 +120,126 @@ public final class Eia608Parser { 0xC5, 0xE5, 0xD8, 0xF8, 0x250C, 0x2510, 0x2514, 0x2518 }; + private final LinkedList availableInputBuffers; + private final LinkedList availableOutputBuffers; + private final TreeSet queuedInputBuffers; + private final ParsableBitArray seiBuffer; - private final StringBuilder stringBuilder; + private final StringBuilder textStringBuilder; private final ArrayList captions; - /* package */ Eia608Parser() { + private final StringBuilder captionStringBuilder; + + private SubtitleInputBuffer dequeuedInputBuffer; + + private int captionMode; + private int captionRowCount; + private String captionString; + private ClosedCaptionCtrl repeatableControl; + + public Eia608Parser() { + availableInputBuffers = new LinkedList<>(); + for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { + availableInputBuffers.add(new SubtitleInputBuffer()); + } + availableOutputBuffers = new LinkedList<>(); + for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) { + availableOutputBuffers.add(new Eia608SubtitleOutputBuffer(this)); + } + queuedInputBuffers = new TreeSet<>(); + seiBuffer = new ParsableBitArray(); - stringBuilder = new StringBuilder(); + textStringBuilder = new StringBuilder(); captions = new ArrayList<>(); + + captionStringBuilder = new StringBuilder(); + + setCaptionMode(CC_MODE_UNKNOWN); + captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT; } - /* package */ boolean canParse(String mimeType) { - return mimeType.equals(MimeTypes.APPLICATION_EIA608); + @Override + public SubtitleInputBuffer dequeueInputBuffer() throws ParserException { + Assertions.checkState(dequeuedInputBuffer == null); + if (availableInputBuffers.isEmpty()) { + return null; + } + dequeuedInputBuffer = availableInputBuffers.pollFirst(); + return dequeuedInputBuffer; } - /* package */ ClosedCaptionList parse(DecoderInputBuffer buffer) { - if (buffer.size < 10) { + @Override + public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws ParserException { + Assertions.checkArgument(inputBuffer != null); + Assertions.checkArgument(inputBuffer == dequeuedInputBuffer); + queuedInputBuffers.add(inputBuffer); + dequeuedInputBuffer = null; + } + + @Override + public SubtitleOutputBuffer dequeueOutputBuffer() throws ParserException { + if (queuedInputBuffers.isEmpty() || availableOutputBuffers.isEmpty()) { + return null; + } + + SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); + SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst(); + + // TODO: investigate ways of batching multiple SubtitleInputBuffers into a single + // SubtitleOutputBuffer + if (inputBuffer.isEndOfStream()) { + outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM); + return outputBuffer; + } + ClosedCaptionList captionList = decode(inputBuffer); + Eia608Subtitle subtitle = generateSubtitle(captionList); + outputBuffer.setOutput(inputBuffer.timeUs, subtitle, 0); + if (inputBuffer.isDecodeOnly()) { + outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY); + } + releaseInputBuffer(inputBuffer); + return outputBuffer; + } + + private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) { + inputBuffer.clear(); + availableInputBuffers.add(inputBuffer); + } + + protected void releaseOutputBuffer(SubtitleOutputBuffer outputBuffer) { + outputBuffer.clear(); + availableOutputBuffers.add(outputBuffer); + } + + @Override + public void flush() { + setCaptionMode(CC_MODE_UNKNOWN); + captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT; + captionStringBuilder.setLength(0); + captionString = null; + repeatableControl = null; + while (!queuedInputBuffers.isEmpty()) { + releaseInputBuffer(queuedInputBuffers.pollFirst()); + } + if (dequeuedInputBuffer != null) { + releaseInputBuffer(dequeuedInputBuffer); + dequeuedInputBuffer = null; + } + } + + @Override + public void release() { + // Do nothing + } + + private ClosedCaptionList decode(SubtitleInputBuffer inputBuffer) { + if (inputBuffer.size < 10) { return null; } captions.clear(); - stringBuilder.setLength(0); - seiBuffer.reset(buffer.data.array()); + textStringBuilder.setLength(0); + seiBuffer.reset(inputBuffer.data.array()); // country_code (8) + provider_code (16) + user_identifier (32) + user_data_type_code (8) + // reserved (1) + process_cc_data_flag (1) + zero_bit (1) @@ -157,7 +273,7 @@ public final class Eia608Parser { // ccData2 - P|0|1|1|X|X|X|X if ((ccData1 == 0x11 || ccData1 == 0x19) && ((ccData2 & 0x70) == 0x30)) { - stringBuilder.append(getSpecialChar(ccData2)); + textStringBuilder.append(getSpecialChar(ccData2)); continue; } @@ -166,7 +282,7 @@ public final class Eia608Parser { if ((ccData1 == 0x12 || ccData1 == 0x1A) && ((ccData2 & 0x60) == 0x20)) { backspace(); // Remove standard equivalent of the special extended char. - stringBuilder.append(getExtendedEsFrChar(ccData2)); + textStringBuilder.append(getExtendedEsFrChar(ccData2)); continue; } @@ -175,7 +291,7 @@ public final class Eia608Parser { if ((ccData1 == 0x13 || ccData1 == 0x1B) && ((ccData2 & 0x60) == 0x20)) { backspace(); // Remove standard equivalent of the special extended char. - stringBuilder.append(getExtendedPtDeChar(ccData2)); + textStringBuilder.append(getExtendedPtDeChar(ccData2)); continue; } @@ -186,9 +302,9 @@ public final class Eia608Parser { } // Basic North American character set. - stringBuilder.append(getChar(ccData1)); + textStringBuilder.append(getChar(ccData1)); if (ccData2 >= 0x20) { - stringBuilder.append(getChar(ccData2)); + textStringBuilder.append(getChar(ccData2)); } } @@ -200,7 +316,153 @@ public final class Eia608Parser { ClosedCaption[] captionArray = new ClosedCaption[captions.size()]; captions.toArray(captionArray); - return new ClosedCaptionList(buffer.timeUs, buffer.isDecodeOnly(), captionArray); + return new ClosedCaptionList(inputBuffer.timeUs, captionArray); + } + + public Eia608Subtitle generateSubtitle(ClosedCaptionList captionList) { + int captionBufferSize = (captionList == null) ? 0 : captionList.captions.length; + if (captionBufferSize != 0) { + boolean isRepeatableControl = false; + for (ClosedCaption caption : captionList.captions) { + if (caption.type == ClosedCaption.TYPE_CTRL) { + ClosedCaptionCtrl captionCtrl = (ClosedCaptionCtrl) caption; + isRepeatableControl = captionBufferSize == 1 && captionCtrl.isRepeatable(); + if (isRepeatableControl && repeatableControl != null + && repeatableControl.cc1 == captionCtrl.cc1 + && repeatableControl.cc2 == captionCtrl.cc2) { + repeatableControl = null; + continue; + } else if (isRepeatableControl) { + repeatableControl = captionCtrl; + } + if (captionCtrl.isMiscCode()) { + handleMiscCode(captionCtrl); + } else if (captionCtrl.isPreambleAddressCode()) { + handlePreambleAddressCode(); + } + } else { + handleText((ClosedCaptionText) caption); + } + } + + if (!isRepeatableControl) { + repeatableControl = null; + } + if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) { + captionString = getDisplayCaption(); + } + } + + return new Eia608Subtitle(captionString); + } + + private void handleMiscCode(ClosedCaptionCtrl caption) { + switch (caption.cc2) { + case ClosedCaptionCtrl.ROLL_UP_CAPTIONS_2_ROWS: + captionRowCount = 2; + setCaptionMode(CC_MODE_ROLL_UP); + return; + case ClosedCaptionCtrl.ROLL_UP_CAPTIONS_3_ROWS: + captionRowCount = 3; + setCaptionMode(CC_MODE_ROLL_UP); + return; + case ClosedCaptionCtrl.ROLL_UP_CAPTIONS_4_ROWS: + captionRowCount = 4; + setCaptionMode(CC_MODE_ROLL_UP); + return; + case ClosedCaptionCtrl.RESUME_CAPTION_LOADING: + setCaptionMode(CC_MODE_POP_ON); + return; + case ClosedCaptionCtrl.RESUME_DIRECT_CAPTIONING: + setCaptionMode(CC_MODE_PAINT_ON); + return; + } + + if (captionMode == CC_MODE_UNKNOWN) { + return; + } + + switch (caption.cc2) { + case ClosedCaptionCtrl.ERASE_DISPLAYED_MEMORY: + captionString = null; + if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) { + captionStringBuilder.setLength(0); + } + return; + case ClosedCaptionCtrl.ERASE_NON_DISPLAYED_MEMORY: + captionStringBuilder.setLength(0); + return; + case ClosedCaptionCtrl.END_OF_CAPTION: + captionString = getDisplayCaption(); + captionStringBuilder.setLength(0); + return; + case ClosedCaptionCtrl.CARRIAGE_RETURN: + maybeAppendNewline(); + return; + case ClosedCaptionCtrl.BACKSPACE: + if (captionStringBuilder.length() > 0) { + captionStringBuilder.setLength(captionStringBuilder.length() - 1); + } + return; + } + } + + private void handlePreambleAddressCode() { + // TODO: Add better handling of this with specific positioning. + maybeAppendNewline(); + } + + private void handleText(ClosedCaptionText caption) { + captionStringBuilder.append(caption.text); + } + + private void maybeAppendNewline() { + int buildLength = captionStringBuilder.length(); + if (buildLength > 0 && captionStringBuilder.charAt(buildLength - 1) != '\n') { + captionStringBuilder.append('\n'); + } + } + + private String getDisplayCaption() { + int buildLength = captionStringBuilder.length(); + if (buildLength == 0) { + return null; + } + + boolean endsWithNewline = captionStringBuilder.charAt(buildLength - 1) == '\n'; + if (buildLength == 1 && endsWithNewline) { + return null; + } + + int endIndex = endsWithNewline ? buildLength - 1 : buildLength; + if (captionMode != CC_MODE_ROLL_UP) { + return captionStringBuilder.substring(0, endIndex); + } + + int startIndex = 0; + int searchBackwardFromIndex = endIndex; + for (int i = 0; i < captionRowCount && searchBackwardFromIndex != -1; i++) { + searchBackwardFromIndex = captionStringBuilder.lastIndexOf("\n", searchBackwardFromIndex - 1); + } + if (searchBackwardFromIndex != -1) { + startIndex = searchBackwardFromIndex + 1; + } + captionStringBuilder.delete(0, startIndex); + return captionStringBuilder.substring(0, endIndex - startIndex); + } + + private void setCaptionMode(int captionMode) { + if (this.captionMode == captionMode) { + return; + } + + this.captionMode = captionMode; + // Clear the working memory. + captionStringBuilder.setLength(0); + if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_UNKNOWN) { + // When switching to roll-up or unknown, we also need to clear the caption. + captionString = null; + } } private static char getChar(byte ccData) { @@ -224,9 +486,10 @@ public final class Eia608Parser { } private void addBufferedText() { - if (stringBuilder.length() > 0) { - captions.add(new ClosedCaptionText(stringBuilder.toString())); - stringBuilder.setLength(0); + if (textStringBuilder.length() > 0) { + String textSnippet = textStringBuilder.toString(); + captions.add(new ClosedCaptionText(textSnippet)); + textStringBuilder.setLength(0); } } diff --git a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608Subtitle.java b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608Subtitle.java new file mode 100644 index 0000000000..f4d4505d66 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608Subtitle.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer.text.eia608; + +import com.google.android.exoplayer.text.Cue; +import com.google.android.exoplayer.text.Subtitle; + +import java.util.Collections; +import java.util.List; + +/** + * A representation of an EIA-608 subtitle. + */ +public final class Eia608Subtitle implements Subtitle { + + private final String caption; + + public Eia608Subtitle(String caption) { + this.caption = caption; + } + + @Override + public int getNextEventTimeIndex(long timeUs) { + return 0; + } + + @Override + public int getEventTimeCount() { + return 1; + } + + @Override + public long getEventTime(int index) { + return 0; + } + + @Override + public List getCues(long timeUs) { + if (caption == null || caption.isEmpty()) { + return Collections.emptyList(); + } else { + return Collections.singletonList(new Cue(caption)); + } + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608SubtitleOutputBuffer.java b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608SubtitleOutputBuffer.java new file mode 100644 index 0000000000..8fb6470d5d --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608SubtitleOutputBuffer.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer.text.eia608; + +import com.google.android.exoplayer.text.Subtitle; +import com.google.android.exoplayer.text.SubtitleOutputBuffer; + +/** + * A {@link Subtitle} output from an {@link Eia608Parser}. + */ +public final class Eia608SubtitleOutputBuffer extends SubtitleOutputBuffer { + + private Eia608Parser owner; + + public Eia608SubtitleOutputBuffer(Eia608Parser owner) { + super(); + this.owner = owner; + } + + @Override + public final void release() { + owner.releaseOutputBuffer(this); + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java deleted file mode 100644 index aa8686f357..0000000000 --- a/library/src/main/java/com/google/android/exoplayer/text/eia608/Eia608TrackRenderer.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer.text.eia608; - -import com.google.android.exoplayer.C; -import com.google.android.exoplayer.DecoderInputBuffer; -import com.google.android.exoplayer.ExoPlaybackException; -import com.google.android.exoplayer.Format; -import com.google.android.exoplayer.FormatHolder; -import com.google.android.exoplayer.TrackRenderer; -import com.google.android.exoplayer.TrackStream; -import com.google.android.exoplayer.text.Cue; -import com.google.android.exoplayer.text.TextRenderer; -import com.google.android.exoplayer.util.Assertions; -import com.google.android.exoplayer.util.Util; - -import android.os.Handler; -import android.os.Handler.Callback; -import android.os.Looper; -import android.os.Message; - -import java.util.Collections; -import java.util.TreeSet; - -/** - * A {@link TrackRenderer} for EIA-608 closed captions in a media stream. - */ -public final class Eia608TrackRenderer extends TrackRenderer implements Callback { - - private static final int MSG_INVOKE_RENDERER = 0; - - private static final int CC_MODE_UNKNOWN = 0; - private static final int CC_MODE_ROLL_UP = 1; - private static final int CC_MODE_POP_ON = 2; - private static final int CC_MODE_PAINT_ON = 3; - - // The default number of rows to display in roll-up captions mode. - private static final int DEFAULT_CAPTIONS_ROW_COUNT = 4; - // The maximum duration that captions are parsed ahead of the current position. - private static final int MAX_BUFFER_READAHEAD_US = 5000000; - - private final Eia608Parser eia608Parser; - private final TextRenderer textRenderer; - private final Handler textRendererHandler; - private final FormatHolder formatHolder; - private final DecoderInputBuffer buffer; - private final StringBuilder captionStringBuilder; - private final TreeSet pendingCaptionLists; - - private boolean inputStreamEnded; - private int captionMode; - private int captionRowCount; - private String caption; - private String lastRenderedCaption; - private ClosedCaptionCtrl repeatableControl; - - /** - * @param textRenderer The text renderer. - * @param textRendererLooper The looper associated with the thread on which textRenderer should be - * invoked. If the renderer makes use of standard Android UI components, then this should - * normally be the looper associated with the applications' main thread, which can be - * obtained using {@link android.app.Activity#getMainLooper()}. Null may be passed if the - * renderer should be invoked directly on the player's internal rendering thread. - */ - public Eia608TrackRenderer(TextRenderer textRenderer, Looper textRendererLooper) { - this.textRenderer = Assertions.checkNotNull(textRenderer); - textRendererHandler = textRendererLooper == null ? null : new Handler(textRendererLooper, this); - eia608Parser = new Eia608Parser(); - formatHolder = new FormatHolder(); - buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); - captionStringBuilder = new StringBuilder(); - pendingCaptionLists = new TreeSet<>(); - } - - @Override - protected int supportsFormat(Format format) { - return eia608Parser.canParse(format.sampleMimeType) ? TrackRenderer.FORMAT_HANDLED - : TrackRenderer.FORMAT_UNSUPPORTED_TYPE; - } - - @Override - protected void reset(long positionUs) { - inputStreamEnded = false; - repeatableControl = null; - pendingCaptionLists.clear(); - clearPendingBuffer(); - captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT; - setCaptionMode(CC_MODE_UNKNOWN); - invokeRenderer(null); - } - - @Override - protected void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { - if (isBufferPending()) { - maybeParsePendingBuffer(positionUs); - } - - while (!isBufferPending() && !inputStreamEnded) { - int result = readSource(formatHolder, buffer); - if (result == TrackStream.BUFFER_READ) { - if (buffer.isEndOfStream()) { - inputStreamEnded = true; - } else { - maybeParsePendingBuffer(positionUs); - } - } else if (result == TrackStream.NOTHING_READ) { - break; - } - } - - while (!pendingCaptionLists.isEmpty()) { - if (pendingCaptionLists.first().timeUs > positionUs) { - // We're too early to render any of the pending caption lists. - return; - } - // Remove and consume the next caption list. - ClosedCaptionList nextCaptionList = pendingCaptionLists.pollFirst(); - consumeCaptionList(nextCaptionList); - // Update the renderer, unless the caption list was marked for decoding only. - if (!nextCaptionList.decodeOnly) { - invokeRenderer(caption); - } - } - } - - @Override - protected boolean isEnded() { - return inputStreamEnded; - } - - @Override - protected boolean isReady() { - return true; - } - - private void invokeRenderer(String text) { - if (Util.areEqual(lastRenderedCaption, text)) { - // No change. - return; - } - this.lastRenderedCaption = text; - if (textRendererHandler != null) { - textRendererHandler.obtainMessage(MSG_INVOKE_RENDERER, text).sendToTarget(); - } else { - invokeRendererInternal(text); - } - } - - @SuppressWarnings("unchecked") - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_INVOKE_RENDERER: - invokeRendererInternal((String) msg.obj); - return true; - } - return false; - } - - private void invokeRendererInternal(String cueText) { - if (cueText == null) { - textRenderer.onCues(Collections.emptyList()); - } else { - textRenderer.onCues(Collections.singletonList(new Cue(cueText))); - } - } - - private void maybeParsePendingBuffer(long positionUs) { - if (buffer.timeUs > positionUs + MAX_BUFFER_READAHEAD_US) { - // We're too early to parse the buffer. - return; - } - ClosedCaptionList holder = eia608Parser.parse(buffer); - clearPendingBuffer(); - if (holder != null) { - pendingCaptionLists.add(holder); - } - } - - private void consumeCaptionList(ClosedCaptionList captionList) { - int captionBufferSize = captionList.captions.length; - if (captionBufferSize == 0) { - return; - } - - boolean isRepeatableControl = false; - for (int i = 0; i < captionBufferSize; i++) { - ClosedCaption caption = captionList.captions[i]; - if (caption.type == ClosedCaption.TYPE_CTRL) { - ClosedCaptionCtrl captionCtrl = (ClosedCaptionCtrl) caption; - isRepeatableControl = captionBufferSize == 1 && captionCtrl.isRepeatable(); - if (isRepeatableControl && repeatableControl != null - && repeatableControl.cc1 == captionCtrl.cc1 - && repeatableControl.cc2 == captionCtrl.cc2) { - repeatableControl = null; - continue; - } else if (isRepeatableControl) { - repeatableControl = captionCtrl; - } - if (captionCtrl.isMiscCode()) { - handleMiscCode(captionCtrl); - } else if (captionCtrl.isPreambleAddressCode()) { - handlePreambleAddressCode(); - } - } else { - handleText((ClosedCaptionText) caption); - } - } - - if (!isRepeatableControl) { - repeatableControl = null; - } - if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) { - caption = getDisplayCaption(); - } - } - - private void handleText(ClosedCaptionText captionText) { - if (captionMode != CC_MODE_UNKNOWN) { - captionStringBuilder.append(captionText.text); - } - } - - private void handleMiscCode(ClosedCaptionCtrl captionCtrl) { - switch (captionCtrl.cc2) { - case ClosedCaptionCtrl.ROLL_UP_CAPTIONS_2_ROWS: - captionRowCount = 2; - setCaptionMode(CC_MODE_ROLL_UP); - return; - case ClosedCaptionCtrl.ROLL_UP_CAPTIONS_3_ROWS: - captionRowCount = 3; - setCaptionMode(CC_MODE_ROLL_UP); - return; - case ClosedCaptionCtrl.ROLL_UP_CAPTIONS_4_ROWS: - captionRowCount = 4; - setCaptionMode(CC_MODE_ROLL_UP); - return; - case ClosedCaptionCtrl.RESUME_CAPTION_LOADING: - setCaptionMode(CC_MODE_POP_ON); - return; - case ClosedCaptionCtrl.RESUME_DIRECT_CAPTIONING: - setCaptionMode(CC_MODE_PAINT_ON); - return; - } - - if (captionMode == CC_MODE_UNKNOWN) { - return; - } - - switch (captionCtrl.cc2) { - case ClosedCaptionCtrl.ERASE_DISPLAYED_MEMORY: - caption = null; - if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_PAINT_ON) { - captionStringBuilder.setLength(0); - } - return; - case ClosedCaptionCtrl.ERASE_NON_DISPLAYED_MEMORY: - captionStringBuilder.setLength(0); - return; - case ClosedCaptionCtrl.END_OF_CAPTION: - caption = getDisplayCaption(); - captionStringBuilder.setLength(0); - return; - case ClosedCaptionCtrl.CARRIAGE_RETURN: - maybeAppendNewline(); - return; - case ClosedCaptionCtrl.BACKSPACE: - if (captionStringBuilder.length() > 0) { - captionStringBuilder.setLength(captionStringBuilder.length() - 1); - } - return; - } - } - - private void handlePreambleAddressCode() { - // TODO: Add better handling of this with specific positioning. - maybeAppendNewline(); - } - - private void setCaptionMode(int captionMode) { - if (this.captionMode == captionMode) { - return; - } - - this.captionMode = captionMode; - // Clear the working memory. - captionStringBuilder.setLength(0); - if (captionMode == CC_MODE_ROLL_UP || captionMode == CC_MODE_UNKNOWN) { - // When switching to roll-up or unknown, we also need to clear the caption. - caption = null; - } - } - - private void maybeAppendNewline() { - int buildLength = captionStringBuilder.length(); - if (buildLength > 0 && captionStringBuilder.charAt(buildLength - 1) != '\n') { - captionStringBuilder.append('\n'); - } - } - - private String getDisplayCaption() { - int buildLength = captionStringBuilder.length(); - if (buildLength == 0) { - return null; - } - - boolean endsWithNewline = captionStringBuilder.charAt(buildLength - 1) == '\n'; - if (buildLength == 1 && endsWithNewline) { - return null; - } - - int endIndex = endsWithNewline ? buildLength - 1 : buildLength; - if (captionMode != CC_MODE_ROLL_UP) { - return captionStringBuilder.substring(0, endIndex); - } - - int startIndex = 0; - int searchBackwardFromIndex = endIndex; - for (int i = 0; i < captionRowCount && searchBackwardFromIndex != -1; i++) { - searchBackwardFromIndex = captionStringBuilder.lastIndexOf("\n", searchBackwardFromIndex - 1); - } - if (searchBackwardFromIndex != -1) { - startIndex = searchBackwardFromIndex + 1; - } - captionStringBuilder.delete(0, startIndex); - return captionStringBuilder.substring(0, endIndex - startIndex); - } - - private void clearPendingBuffer() { - buffer.clear(); - buffer.timeUs = C.UNSET_TIME_US; - } - - private boolean isBufferPending() { - return buffer.timeUs != C.UNSET_TIME_US; - } - -} diff --git a/library/src/main/java/com/google/android/exoplayer/text/subrip/SubripParser.java b/library/src/main/java/com/google/android/exoplayer/text/subrip/SubripParser.java index 39c80949f8..f39d49b5f4 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/subrip/SubripParser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/subrip/SubripParser.java @@ -16,7 +16,7 @@ package com.google.android.exoplayer.text.subrip; import com.google.android.exoplayer.text.Cue; -import com.google.android.exoplayer.text.SubtitleParser; +import com.google.android.exoplayer.text.SimpleSubtitleParser; import com.google.android.exoplayer.util.LongArray; import com.google.android.exoplayer.util.ParsableByteArray; @@ -32,7 +32,7 @@ import java.util.regex.Pattern; /** * A simple SubRip parser. */ -public final class SubripParser extends SubtitleParser { +public final class SubripParser extends SimpleSubtitleParser { private static final String TAG = "SubripParser"; diff --git a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java index abe3fceb19..64f2d09c01 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java @@ -17,7 +17,7 @@ package com.google.android.exoplayer.text.ttml; import com.google.android.exoplayer.C; import com.google.android.exoplayer.ParserException; -import com.google.android.exoplayer.text.SubtitleParser; +import com.google.android.exoplayer.text.SimpleSubtitleParser; import com.google.android.exoplayer.util.ColorParser; import com.google.android.exoplayer.util.ParserUtil; import com.google.android.exoplayer.util.Util; @@ -58,7 +58,7 @@ import java.util.regex.Pattern; *

    * @see TTML specification */ -public final class TtmlParser extends SubtitleParser { +public final class TtmlParser extends SimpleSubtitleParser { private static final String TAG = "TtmlParser"; diff --git a/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gParser.java b/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gParser.java index 1a23ab3cdf..b2259ab085 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gParser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gParser.java @@ -16,15 +16,15 @@ package com.google.android.exoplayer.text.tx3g; import com.google.android.exoplayer.text.Cue; +import com.google.android.exoplayer.text.SimpleSubtitleParser; import com.google.android.exoplayer.text.Subtitle; -import com.google.android.exoplayer.text.SubtitleParser; /** - * A {@link SubtitleParser} for tx3g. + * A subtitle parser for tx3g. *

    * Currently only supports parsing of a single text track. */ -public final class Tx3gParser extends SubtitleParser { +public final class Tx3gParser extends SimpleSubtitleParser { @Override protected Subtitle decode(byte[] bytes, int length) { diff --git a/library/src/main/java/com/google/android/exoplayer/text/webvtt/Mp4WebvttParser.java b/library/src/main/java/com/google/android/exoplayer/text/webvtt/Mp4WebvttParser.java index a6b3f6a405..82da06ae84 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/webvtt/Mp4WebvttParser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/webvtt/Mp4WebvttParser.java @@ -17,7 +17,7 @@ package com.google.android.exoplayer.text.webvtt; import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.text.Cue; -import com.google.android.exoplayer.text.SubtitleParser; +import com.google.android.exoplayer.text.SimpleSubtitleParser; import com.google.android.exoplayer.util.ParsableByteArray; import com.google.android.exoplayer.util.Util; @@ -26,9 +26,9 @@ import java.util.Collections; import java.util.List; /** - * A {@link SubtitleParser} for Webvtt embedded in a Mp4 container file. + * A subtitle parser for Webvtt embedded in a Mp4 container file. */ -public final class Mp4WebvttParser extends SubtitleParser { +public final class Mp4WebvttParser extends SimpleSubtitleParser { private static final int BOX_HEADER_SIZE = 8; diff --git a/library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParser.java b/library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParser.java index a4bed9d25a..6dfd59d7f4 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParser.java @@ -16,7 +16,7 @@ package com.google.android.exoplayer.text.webvtt; import com.google.android.exoplayer.ParserException; -import com.google.android.exoplayer.text.SubtitleParser; +import com.google.android.exoplayer.text.SimpleSubtitleParser; import com.google.android.exoplayer.util.ParsableByteArray; import android.text.TextUtils; @@ -29,7 +29,7 @@ import java.util.List; *

    * @see WebVTT specification */ -public final class WebvttParser extends SubtitleParser { +public final class WebvttParser extends SimpleSubtitleParser { private static final int NO_EVENT_FOUND = -1; private static final int END_OF_FILE_FOUND = 0; diff --git a/library/src/main/java/com/google/android/exoplayer/util/extensions/AudioDecoderTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/util/extensions/AudioDecoderTrackRenderer.java index af4e2d8976..5aedb06c07 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/extensions/AudioDecoderTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/util/extensions/AudioDecoderTrackRenderer.java @@ -132,7 +132,6 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements notifyDecoderError(e); throw ExoPlaybackException.createForRenderer(e, getIndex()); } - decoder.start(); codecCounters.codecInitCount++; } diff --git a/library/src/main/java/com/google/android/exoplayer/util/extensions/SimpleDecoder.java b/library/src/main/java/com/google/android/exoplayer/util/extensions/SimpleDecoder.java index f9618f4880..c85287d492 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/extensions/SimpleDecoder.java +++ b/library/src/main/java/com/google/android/exoplayer/util/extensions/SimpleDecoder.java @@ -25,7 +25,7 @@ import java.util.LinkedList; * Base class for {@link Decoder}s that use their own decode thread. */ public abstract class SimpleDecoder extends Thread implements Decoder { + E extends Exception> implements Decoder { /** * Listener for {@link SimpleDecoder} events. @@ -41,6 +41,8 @@ public abstract class SimpleDecoder queuedInputBuffers; private final LinkedList queuedOutputBuffers; @@ -73,6 +75,13 @@ public abstract class SimpleDecoder