From 6b599751c6b4ee719131632c88aa718a4eb84608 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Mon, 4 Jan 2016 09:07:05 -0800 Subject: [PATCH] Refactored the SubtitleParser's parse() signature ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=111326567 --- .../android/exoplayer/testutil/TestUtil.java | 9 +++ .../text/mp4webvtt/Mp4WebvttParserTest.java | 19 +++---- .../text/subrip/SubripParserTest.java | 31 +++++----- .../exoplayer/text/ttml/TtmlParserTest.java | 7 +-- .../text/webvtt/WebvttParserTest.java | 42 ++++++-------- .../exoplayer/util/ParsableByteArrayTest.java | 57 +++++++++++++++++++ .../exoplayer/hls/WebvttExtractor.java | 14 ++--- .../exoplayer/text/SubtitleParser.java | 15 ++--- .../exoplayer/text/SubtitleParserHelper.java | 10 ++-- .../text/mp4webvtt/Mp4WebvttParser.java | 15 ++--- .../exoplayer/text/subrip/SubripParser.java | 17 +++--- .../exoplayer/text/ttml/TtmlParser.java | 8 ++- .../exoplayer/text/tx3g/Tx3gParser.java | 9 +-- .../exoplayer/text/webvtt/WebvttParser.java | 12 ++-- .../text/webvtt/WebvttParserUtil.java | 9 +-- .../exoplayer/util/ParsableByteArray.java | 34 +++++++++++ .../android/exoplayer/util/ParserUtil.java | 1 + 17 files changed, 188 insertions(+), 121 deletions(-) diff --git a/library/src/androidTest/java/com/google/android/exoplayer/testutil/TestUtil.java b/library/src/androidTest/java/com/google/android/exoplayer/testutil/TestUtil.java index 073b67dd8a..0195028a76 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/testutil/TestUtil.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/testutil/TestUtil.java @@ -21,13 +21,16 @@ import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.ExtractorInput; import com.google.android.exoplayer.extractor.PositionHolder; import com.google.android.exoplayer.upstream.DataSpec; +import com.google.android.exoplayer.util.Util; +import android.app.Instrumentation; import android.net.Uri; import android.test.InstrumentationTestCase; import org.mockito.MockitoAnnotations; import java.io.IOException; +import java.io.InputStream; import java.util.Arrays; import java.util.Random; @@ -115,4 +118,10 @@ public class TestUtil { MockitoAnnotations.initMocks(instrumentationTestCase); } + public static byte[] getByteArray(Instrumentation instrumentation, String fileName) + throws IOException { + InputStream is = instrumentation.getContext().getResources().getAssets().open(fileName); + return Util.toByteArray(is); + } + } diff --git a/library/src/androidTest/java/com/google/android/exoplayer/text/mp4webvtt/Mp4WebvttParserTest.java b/library/src/androidTest/java/com/google/android/exoplayer/text/mp4webvtt/Mp4WebvttParserTest.java index 5bc82782c3..05f24de511 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/text/mp4webvtt/Mp4WebvttParserTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/text/mp4webvtt/Mp4WebvttParserTest.java @@ -24,7 +24,6 @@ import android.util.ArraySet; import junit.framework.TestCase; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Arrays; import java.util.List; @@ -107,29 +106,29 @@ public final class Mp4WebvttParserTest extends TestCase { // Positive tests. - public void testSingleCueSample() throws IOException { - Subtitle result = parser.parse(new ByteArrayInputStream(SINGLE_CUE_SAMPLE)); + public void testSingleCueSample() throws ParserException { + Subtitle result = parser.parse(SINGLE_CUE_SAMPLE, 0, SINGLE_CUE_SAMPLE.length); Cue expectedCue = new Cue("Hello World"); // Line feed must be trimmed by the parser assertMp4WebvttSubtitleEquals(result, expectedCue); } - public void testTwoCuesSample() throws IOException { - Subtitle result = parser.parse(new ByteArrayInputStream(DOUBLE_CUE_SAMPLE)); + public void testTwoCuesSample() throws ParserException { + Subtitle result = parser.parse(DOUBLE_CUE_SAMPLE, 0, DOUBLE_CUE_SAMPLE.length); Cue firstExpectedCue = new Cue("Hello World"); Cue secondExpectedCue = new Cue("Bye Bye"); assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue); } public void testNoCueSample() throws IOException { - Subtitle result = parser.parse(new ByteArrayInputStream(NO_CUE_SAMPLE)); + Subtitle result = parser.parse(NO_CUE_SAMPLE, 0, NO_CUE_SAMPLE.length); assertMp4WebvttSubtitleEquals(result, new Cue[] {}); } // Negative tests. - public void testSampleWithVttCueWithNoPayload() throws IOException { + public void testSampleWithVttCueWithNoPayload() { try { - parser.parse(new ByteArrayInputStream(NO_PAYLOAD_CUE_SAMPLE)); + parser.parse(NO_PAYLOAD_CUE_SAMPLE, 0, NO_PAYLOAD_CUE_SAMPLE.length); } catch (ParserException e) { // Expected. return; @@ -137,9 +136,9 @@ public final class Mp4WebvttParserTest extends TestCase { fail("The parser should have failed, no payload was included in the VTTCue."); } - public void testSampleWithIncompleteHeader() throws IOException { + public void testSampleWithIncompleteHeader() { try { - parser.parse(new ByteArrayInputStream(INCOMPLETE_HEADER_SAMPLE)); + parser.parse(INCOMPLETE_HEADER_SAMPLE, 0, INCOMPLETE_HEADER_SAMPLE.length); } catch (ParserException e) { return; } 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 8b7f74ad1e..a5be4829ba 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 @@ -15,10 +15,11 @@ */ package com.google.android.exoplayer.text.subrip; +import com.google.android.exoplayer.testutil.TestUtil; + import android.test.InstrumentationTestCase; import java.io.IOException; -import java.io.InputStream; /** * Unit test for {@link SubripParser}. @@ -34,8 +35,8 @@ public final class SubripParserTest extends InstrumentationTestCase { public void testParseEmpty() throws IOException { SubripParser parser = new SubripParser(); - InputStream inputStream = getInputStream(EMPTY_FILE); - SubripSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE); + SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length); // Assert that the subtitle is empty. assertEquals(0, subtitle.getEventTimeCount()); assertTrue(subtitle.getCues(0).isEmpty()); @@ -43,8 +44,8 @@ public final class SubripParserTest extends InstrumentationTestCase { public void testParseTypical() throws IOException { SubripParser parser = new SubripParser(); - InputStream inputStream = getInputStream(TYPICAL_FILE); - SubripSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_FILE); + SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length); assertEquals(6, subtitle.getEventTimeCount()); assertTypicalCue1(subtitle, 0); assertTypicalCue2(subtitle, 2); @@ -53,8 +54,8 @@ public final class SubripParserTest extends InstrumentationTestCase { public void testParseTypicalExtraBlankLine() throws IOException { SubripParser parser = new SubripParser(); - InputStream inputStream = getInputStream(TYPICAL_EXTRA_BLANK_LINE); - SubripSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_EXTRA_BLANK_LINE); + SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length); assertEquals(6, subtitle.getEventTimeCount()); assertTypicalCue1(subtitle, 0); assertTypicalCue2(subtitle, 2); @@ -64,8 +65,8 @@ public final class SubripParserTest extends InstrumentationTestCase { public void testParseTypicalMissingTimecode() throws IOException { // Parsing should succeed, parsing the first and third cues only. SubripParser parser = new SubripParser(); - InputStream inputStream = getInputStream(TYPICAL_MISSING_TIMECODE); - SubripSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_MISSING_TIMECODE); + SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length); assertEquals(4, subtitle.getEventTimeCount()); assertTypicalCue1(subtitle, 0); assertTypicalCue3(subtitle, 2); @@ -74,8 +75,8 @@ public final class SubripParserTest extends InstrumentationTestCase { public void testParseTypicalMissingSequence() throws IOException { // Parsing should succeed, parsing the first and third cues only. SubripParser parser = new SubripParser(); - InputStream inputStream = getInputStream(TYPICAL_MISSING_SEQUENCE); - SubripSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_MISSING_SEQUENCE); + SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length); assertEquals(4, subtitle.getEventTimeCount()); assertTypicalCue1(subtitle, 0); assertTypicalCue3(subtitle, 2); @@ -83,8 +84,8 @@ public final class SubripParserTest extends InstrumentationTestCase { public void testParseNoEndTimecodes() throws IOException { SubripParser parser = new SubripParser(); - InputStream inputStream = getInputStream(NO_END_TIMECODES_FILE); - SubripSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), NO_END_TIMECODES_FILE); + SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length); // Test event count. assertEquals(3, subtitle.getEventTimeCount()); @@ -105,10 +106,6 @@ public final class SubripParserTest extends InstrumentationTestCase { subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString()); } - private InputStream getInputStream(String fileName) throws IOException { - return getInstrumentation().getContext().getResources().getAssets().open(fileName); - } - private static void assertTypicalCue1(SubripSubtitle subtitle, int eventIndex) { assertEquals(0, subtitle.getEventTime(eventIndex)); assertEquals("This is the first subtitle.", diff --git a/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java b/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java index eef062bce1..64423c33f8 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer.text.ttml; +import com.google.android.exoplayer.testutil.TestUtil; import com.google.android.exoplayer.text.Cue; import android.test.InstrumentationTestCase; @@ -32,7 +33,6 @@ import android.text.style.TypefaceSpan; import android.text.style.UnderlineSpan; import java.io.IOException; -import java.io.InputStream; import java.util.List; import java.util.Map; @@ -471,8 +471,7 @@ public final class TtmlParserTest extends InstrumentationTestCase { private TtmlSubtitle getSubtitle(String file) throws IOException { TtmlParser ttmlParser = new TtmlParser(); - InputStream inputStream = getInstrumentation().getContext() - .getResources().getAssets().open(file); - return (TtmlSubtitle) ttmlParser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), file); + return ttmlParser.parse(bytes, 0, bytes.length); } } diff --git a/library/src/androidTest/java/com/google/android/exoplayer/text/webvtt/WebvttParserTest.java b/library/src/androidTest/java/com/google/android/exoplayer/text/webvtt/WebvttParserTest.java index 07967e7df7..0c45bae33a 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/text/webvtt/WebvttParserTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/text/webvtt/WebvttParserTest.java @@ -15,13 +15,14 @@ */ package com.google.android.exoplayer.text.webvtt; +import com.google.android.exoplayer.ParserException; +import com.google.android.exoplayer.testutil.TestUtil; import com.google.android.exoplayer.text.Cue; import android.test.InstrumentationTestCase; import android.text.Layout.Alignment; import java.io.IOException; -import java.io.InputStream; import java.util.List; /** @@ -39,21 +40,19 @@ public class WebvttParserTest extends InstrumentationTestCase { public void testParseEmpty() throws IOException { WebvttParser parser = new WebvttParser(); - InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() - .open(EMPTY_FILE); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE); try { - parser.parse(inputStream); - fail("Expected IOException"); - } catch (IOException expected) { + parser.parse(bytes, 0, bytes.length); + fail("Expected ParserException"); + } catch (ParserException expected) { // Do nothing. } } public void testParseTypical() throws IOException { WebvttParser parser = new WebvttParser(); - InputStream inputStream = - getInstrumentation().getContext().getResources().getAssets().open(TYPICAL_FILE); - WebvttSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_FILE); + WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length); // test event count assertEquals(4, subtitle.getEventTimeCount()); @@ -65,9 +64,8 @@ public class WebvttParserTest extends InstrumentationTestCase { public void testParseTypicalWithIds() throws IOException { WebvttParser parser = new WebvttParser(); - InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() - .open(TYPICAL_WITH_IDS_FILE); - WebvttSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_WITH_IDS_FILE); + WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length); // test event count assertEquals(4, subtitle.getEventTimeCount()); @@ -79,9 +77,8 @@ public class WebvttParserTest extends InstrumentationTestCase { public void testParseTypicalWithComments() throws IOException { WebvttParser parser = new WebvttParser(); - InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() - .open(TYPICAL_WITH_COMMENTS_FILE); - WebvttSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_WITH_COMMENTS_FILE); + WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length); // test event count assertEquals(4, subtitle.getEventTimeCount()); @@ -93,9 +90,8 @@ public class WebvttParserTest extends InstrumentationTestCase { public void testParseWithTags() throws IOException { WebvttParser parser = new WebvttParser(); - InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() - .open(WITH_TAGS_FILE); - WebvttSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), WITH_TAGS_FILE); + WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length); // test event count assertEquals(8, subtitle.getEventTimeCount()); @@ -109,9 +105,8 @@ public class WebvttParserTest extends InstrumentationTestCase { public void testParseWithPositioning() throws IOException { WebvttParser parser = new WebvttParser(); - InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() - .open(WITH_POSITIONING_FILE); - WebvttSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), WITH_POSITIONING_FILE); + WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length); // test event count assertEquals(10, subtitle.getEventTimeCount()); @@ -135,9 +130,8 @@ public class WebvttParserTest extends InstrumentationTestCase { public void testParseWithBadCueHeader() throws IOException { WebvttParser parser = new WebvttParser(); - InputStream inputStream = - getInstrumentation().getContext().getResources().getAssets().open(WITH_BAD_CUE_HEADER_FILE); - WebvttSubtitle subtitle = parser.parse(inputStream); + byte[] bytes = TestUtil.getByteArray(getInstrumentation(), WITH_BAD_CUE_HEADER_FILE); + WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length); // test event count assertEquals(4, subtitle.getEventTimeCount()); diff --git a/library/src/androidTest/java/com/google/android/exoplayer/util/ParsableByteArrayTest.java b/library/src/androidTest/java/com/google/android/exoplayer/util/ParsableByteArrayTest.java index c406b27930..e19f478fbc 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/util/ParsableByteArrayTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/util/ParsableByteArrayTest.java @@ -322,4 +322,61 @@ public class ParsableByteArrayTest extends TestCase { assertEquals((short) 0xFF02, byteArray.readLittleEndianShort()); } + public void testReadEmptyString() { + byte[] bytes = new byte[0]; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertNull(parser.readLine()); + } + + public void testReadSingleLineWithoutEndingTrail() { + byte[] bytes = new byte[] { + 'f', 'o', 'o' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertEquals("foo", parser.readLine()); + assertNull(parser.readLine()); + } + + public void testReadSingleLineWithEndingLf() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', '\n' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertEquals("foo", parser.readLine()); + assertNull(parser.readLine()); + } + + public void testReadTwoLinesWithLfFollowedByCr() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', '\n', '\r', 'b', 'a', 'r' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertEquals("foo", parser.readLine()); + assertEquals("bar", parser.readLine()); + assertNull(parser.readLine()); + } + + public void testReadThreeLinesWithEmptyLine() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', '\n', '\r', '\n', 'b', 'a', 'r' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertEquals("foo", parser.readLine()); + assertEquals("", parser.readLine()); + assertEquals("bar", parser.readLine()); + assertNull(parser.readLine()); + } + + public void testReadFourLinesWithCrFollowedByLf() { + byte[] bytes = new byte[] { + 'f', 'o', 'o', '\r', '\n', '\n', 'b', 'a', 'r', '\n', '\r' + }; + ParsableByteArray parser = new ParsableByteArray(bytes); + assertEquals("foo", parser.readLine()); + assertEquals("", parser.readLine()); + assertEquals("", parser.readLine()); + assertEquals("bar", parser.readLine()); + assertNull(parser.readLine()); + } + } diff --git a/library/src/main/java/com/google/android/exoplayer/hls/WebvttExtractor.java b/library/src/main/java/com/google/android/exoplayer/hls/WebvttExtractor.java index 0e648a0388..f1bb1e0661 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/WebvttExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/WebvttExtractor.java @@ -31,10 +31,7 @@ import com.google.android.exoplayer.util.ParsableByteArray; import android.text.TextUtils; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStreamReader; import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -111,12 +108,11 @@ import java.util.regex.Pattern; return Extractor.RESULT_END_OF_INPUT; } - private void processSample() throws IOException { - BufferedReader reader = new BufferedReader( - new InputStreamReader(new ByteArrayInputStream(sampleData), C.UTF8_NAME)); + private void processSample() throws ParserException { + ParsableByteArray webvttData = new ParsableByteArray(sampleData); // Validate the first line of the header. - WebvttParserUtil.validateWebvttHeaderLine(reader); + WebvttParserUtil.validateWebvttHeaderLine(webvttData); // Defaults to use if the header doesn't contain an X-TIMESTAMP-MAP header. long vttTimestampUs = 0; @@ -124,7 +120,7 @@ import java.util.regex.Pattern; // Parse the remainder of the header looking for X-TIMESTAMP-MAP. String line; - while (!TextUtils.isEmpty(line = reader.readLine())) { + while (!TextUtils.isEmpty(line = webvttData.readLine())) { if (line.startsWith("X-TIMESTAMP-MAP")) { Matcher localTimestampMatcher = LOCAL_TIMESTAMP.matcher(line); if (!localTimestampMatcher.find()) { @@ -141,7 +137,7 @@ import java.util.regex.Pattern; } // Find the first cue header and parse the start time. - Matcher cueHeaderMatcher = WebvttParserUtil.findNextCueHeader(reader); + Matcher cueHeaderMatcher = WebvttParserUtil.findNextCueHeader(webvttData); if (cueHeaderMatcher == null) { // No cues found. Don't output a sample, but still output a corresponding track. buildTrackOutput(0); diff --git a/library/src/main/java/com/google/android/exoplayer/text/SubtitleParser.java b/library/src/main/java/com/google/android/exoplayer/text/SubtitleParser.java index d02ac46f42..1daa5aa128 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/SubtitleParser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/SubtitleParser.java @@ -15,11 +15,10 @@ */ package com.google.android.exoplayer.text; -import java.io.IOException; -import java.io.InputStream; +import com.google.android.exoplayer.ParserException; /** - * Parses {@link Subtitle}s from {@link InputStream}s. + * Parses {@link Subtitle}s from a byte array. */ public interface SubtitleParser { @@ -32,12 +31,14 @@ public interface SubtitleParser { public boolean canParse(String mimeType); /** - * Parses a {@link Subtitle} from the provided {@link InputStream}. + * Parses a {@link Subtitle} from the provided {@code byte[]}. * - * @param inputStream The stream from which to parse the subtitle. + * @param bytes The array holding the subtitle data. + * @param offset The offset of the subtitle data in bytes. + * @param length The length of the subtitle data in bytes. * @return A parsed representation of the subtitle. - * @throws IOException If a problem occurred reading from the stream. + * @throws ParserException If a problem occurred parsing the subtitle data. */ - public Subtitle parse(InputStream inputStream) throws IOException; + public Subtitle parse(byte[] bytes, int offset, int length) throws ParserException; } diff --git a/library/src/main/java/com/google/android/exoplayer/text/SubtitleParserHelper.java b/library/src/main/java/com/google/android/exoplayer/text/SubtitleParserHelper.java index 952ae04b97..eab6933e9a 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/SubtitleParserHelper.java +++ b/library/src/main/java/com/google/android/exoplayer/text/SubtitleParserHelper.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer.text; import com.google.android.exoplayer.MediaFormat; +import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Util; @@ -25,9 +26,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; /** * Wraps a {@link SubtitleParser}, exposing an interface similar to {@link MediaCodec} for @@ -165,12 +164,11 @@ import java.io.InputStream; private void handleSample(long sampleTimeUs, SampleHolder holder) { Subtitle parsedSubtitle = null; - IOException error = null; + ParserException error = null; RuntimeException runtimeError = null; try { - InputStream inputStream = new ByteArrayInputStream(holder.data.array(), 0, holder.size); - parsedSubtitle = parser.parse(inputStream); - } catch (IOException e) { + parsedSubtitle = parser.parse(holder.data.array(), 0, holder.size); + } catch (ParserException e) { error = e; } catch (RuntimeException e) { runtimeError = e; diff --git a/library/src/main/java/com/google/android/exoplayer/text/mp4webvtt/Mp4WebvttParser.java b/library/src/main/java/com/google/android/exoplayer/text/mp4webvtt/Mp4WebvttParser.java index 797c0d49b0..3c165b90fa 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/mp4webvtt/Mp4WebvttParser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/mp4webvtt/Mp4WebvttParser.java @@ -22,8 +22,6 @@ import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.ParsableByteArray; import com.google.android.exoplayer.util.Util; -import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -38,7 +36,6 @@ public final class Mp4WebvttParser implements SubtitleParser { private static final int TYPE_payl = Util.getIntegerCodeForString("payl"); private final ParsableByteArray sampleData; - private byte[] inputBytesBuffer; public Mp4WebvttParser() { sampleData = new ParsableByteArray(); @@ -50,15 +47,11 @@ public final class Mp4WebvttParser implements SubtitleParser { } @Override - public Mp4WebvttSubtitle parse(InputStream inputStream) throws IOException { + public Mp4WebvttSubtitle parse(byte[] bytes, int offset, int length) throws ParserException { // Webvtt in Mp4 samples have boxes inside of them, so we have to do a traditional box parsing: // first 4 bytes size and then 4 bytes type. - int inputStreamByteCount = inputStream.available(); - if (inputBytesBuffer == null || inputBytesBuffer.length < inputStreamByteCount) { - inputBytesBuffer = new byte[inputStreamByteCount]; - } - inputStream.read(inputBytesBuffer, 0, inputStreamByteCount); - sampleData.reset(inputBytesBuffer, inputStreamByteCount); + sampleData.reset(bytes, offset + length); + sampleData.setPosition(offset); List resultingCueList = new ArrayList<>(); while (sampleData.bytesLeft() > 0) { if (sampleData.bytesLeft() < BOX_HEADER_SIZE) { @@ -76,7 +69,7 @@ public final class Mp4WebvttParser implements SubtitleParser { return new Mp4WebvttSubtitle(resultingCueList); } - private static Cue parseVttCueBox(ParsableByteArray sampleData) throws IOException { + private static Cue parseVttCueBox(ParsableByteArray sampleData) throws ParserException { while (sampleData.bytesLeft() > 0) { if (sampleData.bytesLeft() < BOX_HEADER_SIZE) { throw new ParserException("Incomplete vtt cue box header found."); 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 9055c1e1ef..81de172d13 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 @@ -15,21 +15,17 @@ */ package com.google.android.exoplayer.text.subrip; -import com.google.android.exoplayer.C; import com.google.android.exoplayer.text.Cue; import com.google.android.exoplayer.text.SubtitleParser; import com.google.android.exoplayer.util.LongArray; import com.google.android.exoplayer.util.MimeTypes; +import com.google.android.exoplayer.util.ParsableByteArray; import android.text.Html; import android.text.Spanned; import android.text.TextUtils; import android.util.Log; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -57,14 +53,15 @@ public final class SubripParser implements SubtitleParser { } @Override - public SubripSubtitle parse(InputStream inputStream) throws IOException { + public SubripSubtitle parse(byte[] bytes, int offset, int length) { ArrayList cues = new ArrayList<>(); LongArray cueTimesUs = new LongArray(); - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, C.UTF8_NAME)); + ParsableByteArray subripData = new ParsableByteArray(bytes, offset + length); + subripData.setPosition(offset); boolean haveEndTimecode; String currentLine; - while ((currentLine = reader.readLine()) != null) { + while ((currentLine = subripData.readLine()) != null) { if (currentLine.length() == 0) { // Skip blank lines. continue; @@ -80,7 +77,7 @@ public final class SubripParser implements SubtitleParser { // Read and parse the timing line. haveEndTimecode = false; - currentLine = reader.readLine(); + currentLine = subripData.readLine(); Matcher matcher = SUBRIP_TIMING_LINE.matcher(currentLine); if (matcher.find()) { cueTimesUs.add(parseTimecode(matcher.group(1))); @@ -96,7 +93,7 @@ public final class SubripParser implements SubtitleParser { // Read and parse the text. textBuilder.setLength(0); - while (!TextUtils.isEmpty(currentLine = reader.readLine())) { + while (!TextUtils.isEmpty(currentLine = subripData.readLine())) { if (textBuilder.length() > 0) { textBuilder.append("
"); } 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 7bbaa9a215..2b089f258a 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,6 @@ 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.Subtitle; import com.google.android.exoplayer.text.SubtitleParser; import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.ParserUtil; @@ -30,8 +29,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; @@ -97,10 +96,11 @@ public final class TtmlParser implements SubtitleParser { } @Override - public Subtitle parse(InputStream inputStream) throws IOException { + public TtmlSubtitle parse(byte[] bytes, int offset, int length) throws ParserException { try { XmlPullParser xmlParser = xmlParserFactory.newPullParser(); Map globalStyles = new HashMap<>(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, offset, length); xmlParser.setInput(inputStream, null); TtmlSubtitle ttmlSubtitle = null; LinkedList nodeStack = new LinkedList<>(); @@ -150,6 +150,8 @@ public final class TtmlParser implements SubtitleParser { return ttmlSubtitle; } catch (XmlPullParserException xppe) { throw new ParserException("Unable to parse source", xppe); + } catch (IOException e) { + throw new IllegalStateException("Unexpected error when reading input.", e); } } 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 4804c2e913..8851d5e057 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 @@ -20,10 +20,6 @@ import com.google.android.exoplayer.text.Subtitle; import com.google.android.exoplayer.text.SubtitleParser; import com.google.android.exoplayer.util.MimeTypes; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; - /** * A {@link SubtitleParser} for tx3g. *

@@ -37,9 +33,8 @@ public final class Tx3gParser implements SubtitleParser { } @Override - public Subtitle parse(InputStream inputStream) throws IOException { - DataInputStream dataInputStream = new DataInputStream(inputStream); - String cueText = dataInputStream.readUTF(); + public Subtitle parse(byte[] bytes, int offset, int length) { + String cueText = new String(bytes, offset, length); return new Tx3gSubtitle(new Cue(cueText)); } 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 aca43b451f..200ba2b437 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 @@ -15,19 +15,16 @@ */ package com.google.android.exoplayer.text.webvtt; -import com.google.android.exoplayer.C; +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.util.MimeTypes; +import com.google.android.exoplayer.util.ParsableByteArray; import android.text.Layout.Alignment; import android.text.TextUtils; import android.util.Log; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -59,8 +56,9 @@ public final class WebvttParser implements SubtitleParser { } @Override - public final WebvttSubtitle parse(InputStream inputStream) throws IOException { - BufferedReader webvttData = new BufferedReader(new InputStreamReader(inputStream, C.UTF8_NAME)); + public final WebvttSubtitle parse(byte[] bytes, int offset, int length) throws ParserException { + ParsableByteArray webvttData = new ParsableByteArray(bytes, offset + length); + webvttData.setPosition(offset); // Validate the first line of the header, and skip the remainder. WebvttParserUtil.validateWebvttHeaderLine(webvttData); diff --git a/library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParserUtil.java b/library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParserUtil.java index 1ecc299196..bcf96b18e6 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParserUtil.java +++ b/library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParserUtil.java @@ -16,9 +16,8 @@ package com.google.android.exoplayer.text.webvtt; import com.google.android.exoplayer.ParserException; +import com.google.android.exoplayer.util.ParsableByteArray; -import java.io.BufferedReader; -import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -38,9 +37,8 @@ public final class WebvttParserUtil { * * @param input The input from which the line should be read. * @throws ParserException If the line isn't the start of a valid WebVTT file. - * @throws IOException If an error occurs reading from the input. */ - public static void validateWebvttHeaderLine(BufferedReader input) throws IOException { + public static void validateWebvttHeaderLine(ParsableByteArray input) throws ParserException { String line = input.readLine(); if (line == null || !HEADER.matcher(line).matches()) { throw new ParserException("Expected WEBVTT. Got " + line); @@ -51,12 +49,11 @@ public final class WebvttParserUtil { * Reads lines up to and including the next WebVTT cue header. * * @param input The input from which lines should be read. - * @throws IOException If an error occurs reading from the input. * @return A {@link Matcher} for the WebVTT cue header, or null if the end of the input was * reached without a cue header being found. In the case that a cue header is found, groups 1, * 2 and 3 of the returned matcher contain the start time, end time and settings list. */ - public static Matcher findNextCueHeader(BufferedReader input) throws IOException { + public static Matcher findNextCueHeader(ParsableByteArray input) { String line; while ((line = input.readLine()) != null) { if (COMMENT.matcher(line).matches()) { diff --git a/library/src/main/java/com/google/android/exoplayer/util/ParsableByteArray.java b/library/src/main/java/com/google/android/exoplayer/util/ParsableByteArray.java index 294db67c42..766150ffb0 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/ParsableByteArray.java +++ b/library/src/main/java/com/google/android/exoplayer/util/ParsableByteArray.java @@ -306,4 +306,38 @@ public final class ParsableByteArray { return result; } + /** + * Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a + * carriage return ('\r'), or a carriage return followed immediately by a line feed. Platform + * default's charset used. + * + * @return A String containing the contents of the line, not including any line-termination + * characters, or null if the end of the stream has been reached. + */ + public String readLine() { + if (bytesLeft() == 0) { + return null; + } + int lineLimit = position; + while (lineLimit < limit && data[lineLimit] != '\n' && data[lineLimit] != '\r') { + lineLimit++; + } + String line = new String(data, position, lineLimit - position); + position = lineLimit; + if (position == limit) { + return line; + } + if (data[position] == '\n') { + position++; + if (position == limit) { + return line; + } + } + if (data[position] == '\r') { + position++; + } + return line; + } + + } diff --git a/library/src/main/java/com/google/android/exoplayer/util/ParserUtil.java b/library/src/main/java/com/google/android/exoplayer/util/ParserUtil.java index 32136889a7..105c5a5838 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/ParserUtil.java +++ b/library/src/main/java/com/google/android/exoplayer/util/ParserUtil.java @@ -47,4 +47,5 @@ public final class ParserUtil { public static String removeNamespacePrefix(String attributeName) { return attributeName.replaceFirst("^.*:", ""); } + }