diff --git a/library/src/androidTest/assets/subrip/no_end_timecodes b/library/src/androidTest/assets/subrip/no_end_timecodes
new file mode 100644
index 0000000000..0a856c6994
--- /dev/null
+++ b/library/src/androidTest/assets/subrip/no_end_timecodes
@@ -0,0 +1,11 @@
+1
+00:00:00,000 -->
+SubRip doesn't technically allow missing end timecodes.
+
+2
+00:00:02,345 -->
+We interpret it to mean that a subtitle extends to the start of the next one.
+
+3
+00:00:03,456 -->
+Or to the end of the media.
\ No newline at end of file
diff --git a/library/src/androidTest/java/com/google/android/exoplayer/text/subrip/SubripParserTest.java b/library/src/androidTest/java/com/google/android/exoplayer/text/subrip/SubripParserTest.java
index c3f6ed1aaa..05958cf496 100644
--- a/library/src/androidTest/java/com/google/android/exoplayer/text/subrip/SubripParserTest.java
+++ b/library/src/androidTest/java/com/google/android/exoplayer/text/subrip/SubripParserTest.java
@@ -25,13 +25,14 @@ import java.io.InputStream;
*/
public final class SubripParserTest extends InstrumentationTestCase {
- private static final String TYPICAL_SUBRIP_FILE = "subrip/typical";
- private static final String EMPTY_SUBRIP_FILE = "subrip/empty";
+ private static final String EMPTY_FILE = "subrip/empty";
+ private static final String TYPICAL_FILE = "subrip/typical";
+ private static final String NO_END_TIMECODES_FILE = "subrip/no_end_timecodes";
- public void testParseNullSubripFile() throws IOException {
+ public void testParseEmptySubripFile() throws IOException {
SubripParser parser = new SubripParser();
InputStream inputStream =
- getInstrumentation().getContext().getResources().getAssets().open(EMPTY_SUBRIP_FILE);
+ getInstrumentation().getContext().getResources().getAssets().open(EMPTY_FILE);
SubripSubtitle subtitle = parser.parse(inputStream);
// Assert that the subtitle is empty.
assertEquals(0, subtitle.getEventTimeCount());
@@ -41,7 +42,7 @@ public final class SubripParserTest extends InstrumentationTestCase {
public void testParseTypicalSubripFile() throws IOException {
SubripParser parser = new SubripParser();
InputStream inputStream =
- getInstrumentation().getContext().getResources().getAssets().open(TYPICAL_SUBRIP_FILE);
+ getInstrumentation().getContext().getResources().getAssets().open(TYPICAL_FILE);
SubripSubtitle subtitle = parser.parse(inputStream);
// Test event count.
@@ -60,4 +61,29 @@ public final class SubripParserTest extends InstrumentationTestCase {
assertEquals(3456000, subtitle.getEventTime(3));
}
+ public void testParseNoEndTimecodes() throws IOException {
+ SubripParser parser = new SubripParser();
+ InputStream inputStream = getInstrumentation().getContext().getResources().getAssets()
+ .open(NO_END_TIMECODES_FILE);
+ SubripSubtitle subtitle = parser.parse(inputStream);
+
+ // Test event count.
+ assertEquals(3, subtitle.getEventTimeCount());
+
+ // Test first cue.
+ assertEquals(0, subtitle.getEventTime(0));
+ assertEquals("SubRip doesn't technically allow missing end timecodes.",
+ subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString());
+
+ // Test second cue.
+ assertEquals(2345000, subtitle.getEventTime(1));
+ assertEquals("We interpret it to mean that a subtitle extends to the start of the next one.",
+ subtitle.getCues(subtitle.getEventTime(1)).get(0).text.toString());
+
+ // Test third cue.
+ assertEquals(3456000, subtitle.getEventTime(2));
+ assertEquals("Or to the end of the media.",
+ subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString());
+ }
+
}
diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java
index ee5fc71662..0b3f06840f 100644
--- a/library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java
+++ b/library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java
@@ -73,6 +73,8 @@ public final class WebmExtractor implements Extractor {
private static final String CODEC_ID_AAC = "A_AAC";
private static final String CODEC_ID_MP3 = "A_MPEG/L3";
private static final String CODEC_ID_AC3 = "A_AC3";
+ private static final String CODEC_ID_SUBRIP = "S_TEXT/UTF8";
+
private static final int VORBIS_MAX_INPUT_SIZE = 8192;
private static final int OPUS_MAX_INPUT_SIZE = 5760;
private static final int MP3_MAX_INPUT_SIZE = 4096;
@@ -98,6 +100,7 @@ public final class WebmExtractor implements Extractor {
private static final int ID_SIMPLE_BLOCK = 0xA3;
private static final int ID_BLOCK_GROUP = 0xA0;
private static final int ID_BLOCK = 0xA1;
+ private static final int ID_BLOCK_DURATION = 0x9B;
private static final int ID_REFERENCE_BLOCK = 0xFB;
private static final int ID_TRACKS = 0x1654AE6B;
private static final int ID_TRACK_ENTRY = 0xAE;
@@ -131,12 +134,39 @@ public final class WebmExtractor implements Extractor {
private static final int ID_CUE_TIME = 0xB3;
private static final int ID_CUE_TRACK_POSITIONS = 0xB7;
private static final int ID_CUE_CLUSTER_POSITION = 0xF1;
+ private static final int ID_LANGUAGE = 0x22B59C;
private static final int LACING_NONE = 0;
private static final int LACING_XIPH = 1;
private static final int LACING_FIXED_SIZE = 2;
private static final int LACING_EBML = 3;
+ /**
+ * A template for the prefix that must be added to each subrip sample. The 12 byte end timecode
+ * starting at {@link #SUBRIP_PREFIX_END_TIMECODE_OFFSET} is set to a dummy value, and must be
+ * replaced with the duration of the subtitle.
+ *
+ * Equivalent to the UTF-8 string: "1\n00:00:00,000 --> 00:00:00,000\n".
+ */
+ private static final byte[] SUBRIP_PREFIX = new byte[] {49, 10, 48, 48, 58, 48, 48, 58, 48, 48,
+ 44, 48, 48, 48, 32, 45, 45, 62, 32, 48, 48, 58, 48, 48, 58, 48, 48, 44, 48, 48, 48, 10};
+ /**
+ * A special end timecode indicating that a subtitle should be displayed until the next subtitle,
+ * or until the end of the media in the case of the last subtitle.
+ *
+ * Equivalent to the UTF-8 string: " ".
+ */
+ private static final byte[] SUBRIP_TIMECODE_EMPTY =
+ new byte[] {32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32};
+ /**
+ * The byte offset of the end timecode in {@link #SUBRIP_PREFIX}.
+ */
+ private static final int SUBRIP_PREFIX_END_TIMECODE_OFFSET = 19;
+ /**
+ * The length in bytes of a timecode in a subrip prefix.
+ */
+ private static final int SUBRIP_TIMECODE_LENGTH = 12;
+
private final EbmlReader reader;
private final VarintReader varintReader;
private final SparseArray