Commit 6b599751 by aquilescanta Committed by Oliver Woodman

Refactored the SubtitleParser's parse() signature

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=111326567
parent 9bcd1069
...@@ -21,13 +21,16 @@ import com.google.android.exoplayer.extractor.Extractor; ...@@ -21,13 +21,16 @@ import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput; import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.PositionHolder; import com.google.android.exoplayer.extractor.PositionHolder;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.util.Util;
import android.app.Instrumentation;
import android.net.Uri; import android.net.Uri;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random; import java.util.Random;
...@@ -115,4 +118,10 @@ public class TestUtil { ...@@ -115,4 +118,10 @@ public class TestUtil {
MockitoAnnotations.initMocks(instrumentationTestCase); 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);
}
} }
...@@ -24,7 +24,6 @@ import android.util.ArraySet; ...@@ -24,7 +24,6 @@ import android.util.ArraySet;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
...@@ -107,29 +106,29 @@ public final class Mp4WebvttParserTest extends TestCase { ...@@ -107,29 +106,29 @@ public final class Mp4WebvttParserTest extends TestCase {
// Positive tests. // Positive tests.
public void testSingleCueSample() throws IOException { public void testSingleCueSample() throws ParserException {
Subtitle result = parser.parse(new ByteArrayInputStream(SINGLE_CUE_SAMPLE)); 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 Cue expectedCue = new Cue("Hello World"); // Line feed must be trimmed by the parser
assertMp4WebvttSubtitleEquals(result, expectedCue); assertMp4WebvttSubtitleEquals(result, expectedCue);
} }
public void testTwoCuesSample() throws IOException { public void testTwoCuesSample() throws ParserException {
Subtitle result = parser.parse(new ByteArrayInputStream(DOUBLE_CUE_SAMPLE)); Subtitle result = parser.parse(DOUBLE_CUE_SAMPLE, 0, DOUBLE_CUE_SAMPLE.length);
Cue firstExpectedCue = new Cue("Hello World"); Cue firstExpectedCue = new Cue("Hello World");
Cue secondExpectedCue = new Cue("Bye Bye"); Cue secondExpectedCue = new Cue("Bye Bye");
assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue); assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue);
} }
public void testNoCueSample() throws IOException { 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[] {}); assertMp4WebvttSubtitleEquals(result, new Cue[] {});
} }
// Negative tests. // Negative tests.
public void testSampleWithVttCueWithNoPayload() throws IOException { public void testSampleWithVttCueWithNoPayload() {
try { try {
parser.parse(new ByteArrayInputStream(NO_PAYLOAD_CUE_SAMPLE)); parser.parse(NO_PAYLOAD_CUE_SAMPLE, 0, NO_PAYLOAD_CUE_SAMPLE.length);
} catch (ParserException e) { } catch (ParserException e) {
// Expected. // Expected.
return; return;
...@@ -137,9 +136,9 @@ public final class Mp4WebvttParserTest extends TestCase { ...@@ -137,9 +136,9 @@ public final class Mp4WebvttParserTest extends TestCase {
fail("The parser should have failed, no payload was included in the VTTCue."); fail("The parser should have failed, no payload was included in the VTTCue.");
} }
public void testSampleWithIncompleteHeader() throws IOException { public void testSampleWithIncompleteHeader() {
try { try {
parser.parse(new ByteArrayInputStream(INCOMPLETE_HEADER_SAMPLE)); parser.parse(INCOMPLETE_HEADER_SAMPLE, 0, INCOMPLETE_HEADER_SAMPLE.length);
} catch (ParserException e) { } catch (ParserException e) {
return; return;
} }
......
...@@ -15,10 +15,11 @@ ...@@ -15,10 +15,11 @@
*/ */
package com.google.android.exoplayer.text.subrip; package com.google.android.exoplayer.text.subrip;
import com.google.android.exoplayer.testutil.TestUtil;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
/** /**
* Unit test for {@link SubripParser}. * Unit test for {@link SubripParser}.
...@@ -34,8 +35,8 @@ public final class SubripParserTest extends InstrumentationTestCase { ...@@ -34,8 +35,8 @@ public final class SubripParserTest extends InstrumentationTestCase {
public void testParseEmpty() throws IOException { public void testParseEmpty() throws IOException {
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
InputStream inputStream = getInputStream(EMPTY_FILE); byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE);
SubripSubtitle subtitle = parser.parse(inputStream); SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
// Assert that the subtitle is empty. // Assert that the subtitle is empty.
assertEquals(0, subtitle.getEventTimeCount()); assertEquals(0, subtitle.getEventTimeCount());
assertTrue(subtitle.getCues(0).isEmpty()); assertTrue(subtitle.getCues(0).isEmpty());
...@@ -43,8 +44,8 @@ public final class SubripParserTest extends InstrumentationTestCase { ...@@ -43,8 +44,8 @@ public final class SubripParserTest extends InstrumentationTestCase {
public void testParseTypical() throws IOException { public void testParseTypical() throws IOException {
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
InputStream inputStream = getInputStream(TYPICAL_FILE); byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_FILE);
SubripSubtitle subtitle = parser.parse(inputStream); SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
assertEquals(6, subtitle.getEventTimeCount()); assertEquals(6, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0); assertTypicalCue1(subtitle, 0);
assertTypicalCue2(subtitle, 2); assertTypicalCue2(subtitle, 2);
...@@ -53,8 +54,8 @@ public final class SubripParserTest extends InstrumentationTestCase { ...@@ -53,8 +54,8 @@ public final class SubripParserTest extends InstrumentationTestCase {
public void testParseTypicalExtraBlankLine() throws IOException { public void testParseTypicalExtraBlankLine() throws IOException {
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
InputStream inputStream = getInputStream(TYPICAL_EXTRA_BLANK_LINE); byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_EXTRA_BLANK_LINE);
SubripSubtitle subtitle = parser.parse(inputStream); SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
assertEquals(6, subtitle.getEventTimeCount()); assertEquals(6, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0); assertTypicalCue1(subtitle, 0);
assertTypicalCue2(subtitle, 2); assertTypicalCue2(subtitle, 2);
...@@ -64,8 +65,8 @@ public final class SubripParserTest extends InstrumentationTestCase { ...@@ -64,8 +65,8 @@ public final class SubripParserTest extends InstrumentationTestCase {
public void testParseTypicalMissingTimecode() throws IOException { public void testParseTypicalMissingTimecode() throws IOException {
// Parsing should succeed, parsing the first and third cues only. // Parsing should succeed, parsing the first and third cues only.
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
InputStream inputStream = getInputStream(TYPICAL_MISSING_TIMECODE); byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_MISSING_TIMECODE);
SubripSubtitle subtitle = parser.parse(inputStream); SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0); assertTypicalCue1(subtitle, 0);
assertTypicalCue3(subtitle, 2); assertTypicalCue3(subtitle, 2);
...@@ -74,8 +75,8 @@ public final class SubripParserTest extends InstrumentationTestCase { ...@@ -74,8 +75,8 @@ public final class SubripParserTest extends InstrumentationTestCase {
public void testParseTypicalMissingSequence() throws IOException { public void testParseTypicalMissingSequence() throws IOException {
// Parsing should succeed, parsing the first and third cues only. // Parsing should succeed, parsing the first and third cues only.
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
InputStream inputStream = getInputStream(TYPICAL_MISSING_SEQUENCE); byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_MISSING_SEQUENCE);
SubripSubtitle subtitle = parser.parse(inputStream); SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0); assertTypicalCue1(subtitle, 0);
assertTypicalCue3(subtitle, 2); assertTypicalCue3(subtitle, 2);
...@@ -83,8 +84,8 @@ public final class SubripParserTest extends InstrumentationTestCase { ...@@ -83,8 +84,8 @@ public final class SubripParserTest extends InstrumentationTestCase {
public void testParseNoEndTimecodes() throws IOException { public void testParseNoEndTimecodes() throws IOException {
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
InputStream inputStream = getInputStream(NO_END_TIMECODES_FILE); byte[] bytes = TestUtil.getByteArray(getInstrumentation(), NO_END_TIMECODES_FILE);
SubripSubtitle subtitle = parser.parse(inputStream); SubripSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
// Test event count. // Test event count.
assertEquals(3, subtitle.getEventTimeCount()); assertEquals(3, subtitle.getEventTimeCount());
...@@ -105,10 +106,6 @@ public final class SubripParserTest extends InstrumentationTestCase { ...@@ -105,10 +106,6 @@ public final class SubripParserTest extends InstrumentationTestCase {
subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString()); 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) { private static void assertTypicalCue1(SubripSubtitle subtitle, int eventIndex) {
assertEquals(0, subtitle.getEventTime(eventIndex)); assertEquals(0, subtitle.getEventTime(eventIndex));
assertEquals("This is the first subtitle.", assertEquals("This is the first subtitle.",
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.text.ttml; package com.google.android.exoplayer.text.ttml;
import com.google.android.exoplayer.testutil.TestUtil;
import com.google.android.exoplayer.text.Cue; import com.google.android.exoplayer.text.Cue;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
...@@ -32,7 +33,6 @@ import android.text.style.TypefaceSpan; ...@@ -32,7 +33,6 @@ import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan; import android.text.style.UnderlineSpan;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -471,8 +471,7 @@ public final class TtmlParserTest extends InstrumentationTestCase { ...@@ -471,8 +471,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
private TtmlSubtitle getSubtitle(String file) throws IOException { private TtmlSubtitle getSubtitle(String file) throws IOException {
TtmlParser ttmlParser = new TtmlParser(); TtmlParser ttmlParser = new TtmlParser();
InputStream inputStream = getInstrumentation().getContext() byte[] bytes = TestUtil.getByteArray(getInstrumentation(), file);
.getResources().getAssets().open(file); return ttmlParser.parse(bytes, 0, bytes.length);
return (TtmlSubtitle) ttmlParser.parse(inputStream);
} }
} }
...@@ -15,13 +15,14 @@ ...@@ -15,13 +15,14 @@
*/ */
package com.google.android.exoplayer.text.webvtt; 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 com.google.android.exoplayer.text.Cue;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import android.text.Layout.Alignment; import android.text.Layout.Alignment;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.List; import java.util.List;
/** /**
...@@ -39,21 +40,19 @@ public class WebvttParserTest extends InstrumentationTestCase { ...@@ -39,21 +40,19 @@ public class WebvttParserTest extends InstrumentationTestCase {
public void testParseEmpty() throws IOException { public void testParseEmpty() throws IOException {
WebvttParser parser = new WebvttParser(); WebvttParser parser = new WebvttParser();
InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE);
.open(EMPTY_FILE);
try { try {
parser.parse(inputStream); parser.parse(bytes, 0, bytes.length);
fail("Expected IOException"); fail("Expected ParserException");
} catch (IOException expected) { } catch (ParserException expected) {
// Do nothing. // Do nothing.
} }
} }
public void testParseTypical() throws IOException { public void testParseTypical() throws IOException {
WebvttParser parser = new WebvttParser(); WebvttParser parser = new WebvttParser();
InputStream inputStream = byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_FILE);
getInstrumentation().getContext().getResources().getAssets().open(TYPICAL_FILE); WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
WebvttSubtitle subtitle = parser.parse(inputStream);
// test event count // test event count
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
...@@ -65,9 +64,8 @@ public class WebvttParserTest extends InstrumentationTestCase { ...@@ -65,9 +64,8 @@ public class WebvttParserTest extends InstrumentationTestCase {
public void testParseTypicalWithIds() throws IOException { public void testParseTypicalWithIds() throws IOException {
WebvttParser parser = new WebvttParser(); WebvttParser parser = new WebvttParser();
InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_WITH_IDS_FILE);
.open(TYPICAL_WITH_IDS_FILE); WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
WebvttSubtitle subtitle = parser.parse(inputStream);
// test event count // test event count
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
...@@ -79,9 +77,8 @@ public class WebvttParserTest extends InstrumentationTestCase { ...@@ -79,9 +77,8 @@ public class WebvttParserTest extends InstrumentationTestCase {
public void testParseTypicalWithComments() throws IOException { public void testParseTypicalWithComments() throws IOException {
WebvttParser parser = new WebvttParser(); WebvttParser parser = new WebvttParser();
InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_WITH_COMMENTS_FILE);
.open(TYPICAL_WITH_COMMENTS_FILE); WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
WebvttSubtitle subtitle = parser.parse(inputStream);
// test event count // test event count
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
...@@ -93,9 +90,8 @@ public class WebvttParserTest extends InstrumentationTestCase { ...@@ -93,9 +90,8 @@ public class WebvttParserTest extends InstrumentationTestCase {
public void testParseWithTags() throws IOException { public void testParseWithTags() throws IOException {
WebvttParser parser = new WebvttParser(); WebvttParser parser = new WebvttParser();
InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() byte[] bytes = TestUtil.getByteArray(getInstrumentation(), WITH_TAGS_FILE);
.open(WITH_TAGS_FILE); WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
WebvttSubtitle subtitle = parser.parse(inputStream);
// test event count // test event count
assertEquals(8, subtitle.getEventTimeCount()); assertEquals(8, subtitle.getEventTimeCount());
...@@ -109,9 +105,8 @@ public class WebvttParserTest extends InstrumentationTestCase { ...@@ -109,9 +105,8 @@ public class WebvttParserTest extends InstrumentationTestCase {
public void testParseWithPositioning() throws IOException { public void testParseWithPositioning() throws IOException {
WebvttParser parser = new WebvttParser(); WebvttParser parser = new WebvttParser();
InputStream inputStream = getInstrumentation().getContext().getResources().getAssets() byte[] bytes = TestUtil.getByteArray(getInstrumentation(), WITH_POSITIONING_FILE);
.open(WITH_POSITIONING_FILE); WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
WebvttSubtitle subtitle = parser.parse(inputStream);
// test event count // test event count
assertEquals(10, subtitle.getEventTimeCount()); assertEquals(10, subtitle.getEventTimeCount());
...@@ -135,9 +130,8 @@ public class WebvttParserTest extends InstrumentationTestCase { ...@@ -135,9 +130,8 @@ public class WebvttParserTest extends InstrumentationTestCase {
public void testParseWithBadCueHeader() throws IOException { public void testParseWithBadCueHeader() throws IOException {
WebvttParser parser = new WebvttParser(); WebvttParser parser = new WebvttParser();
InputStream inputStream = byte[] bytes = TestUtil.getByteArray(getInstrumentation(), WITH_BAD_CUE_HEADER_FILE);
getInstrumentation().getContext().getResources().getAssets().open(WITH_BAD_CUE_HEADER_FILE); WebvttSubtitle subtitle = parser.parse(bytes, 0, bytes.length);
WebvttSubtitle subtitle = parser.parse(inputStream);
// test event count // test event count
assertEquals(4, subtitle.getEventTimeCount()); assertEquals(4, subtitle.getEventTimeCount());
......
...@@ -322,4 +322,61 @@ public class ParsableByteArrayTest extends TestCase { ...@@ -322,4 +322,61 @@ public class ParsableByteArrayTest extends TestCase {
assertEquals((short) 0xFF02, byteArray.readLittleEndianShort()); 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());
}
} }
...@@ -31,10 +31,7 @@ import com.google.android.exoplayer.util.ParsableByteArray; ...@@ -31,10 +31,7 @@ import com.google.android.exoplayer.util.ParsableByteArray;
import android.text.TextUtils; import android.text.TextUtils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays; import java.util.Arrays;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
...@@ -111,12 +108,11 @@ import java.util.regex.Pattern; ...@@ -111,12 +108,11 @@ import java.util.regex.Pattern;
return Extractor.RESULT_END_OF_INPUT; return Extractor.RESULT_END_OF_INPUT;
} }
private void processSample() throws IOException { private void processSample() throws ParserException {
BufferedReader reader = new BufferedReader( ParsableByteArray webvttData = new ParsableByteArray(sampleData);
new InputStreamReader(new ByteArrayInputStream(sampleData), C.UTF8_NAME));
// Validate the first line of the header. // 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. // Defaults to use if the header doesn't contain an X-TIMESTAMP-MAP header.
long vttTimestampUs = 0; long vttTimestampUs = 0;
...@@ -124,7 +120,7 @@ import java.util.regex.Pattern; ...@@ -124,7 +120,7 @@ import java.util.regex.Pattern;
// Parse the remainder of the header looking for X-TIMESTAMP-MAP. // Parse the remainder of the header looking for X-TIMESTAMP-MAP.
String line; String line;
while (!TextUtils.isEmpty(line = reader.readLine())) { while (!TextUtils.isEmpty(line = webvttData.readLine())) {
if (line.startsWith("X-TIMESTAMP-MAP")) { if (line.startsWith("X-TIMESTAMP-MAP")) {
Matcher localTimestampMatcher = LOCAL_TIMESTAMP.matcher(line); Matcher localTimestampMatcher = LOCAL_TIMESTAMP.matcher(line);
if (!localTimestampMatcher.find()) { if (!localTimestampMatcher.find()) {
...@@ -141,7 +137,7 @@ import java.util.regex.Pattern; ...@@ -141,7 +137,7 @@ import java.util.regex.Pattern;
} }
// Find the first cue header and parse the start time. // Find the first cue header and parse the start time.
Matcher cueHeaderMatcher = WebvttParserUtil.findNextCueHeader(reader); Matcher cueHeaderMatcher = WebvttParserUtil.findNextCueHeader(webvttData);
if (cueHeaderMatcher == null) { if (cueHeaderMatcher == null) {
// No cues found. Don't output a sample, but still output a corresponding track. // No cues found. Don't output a sample, but still output a corresponding track.
buildTrackOutput(0); buildTrackOutput(0);
......
...@@ -15,11 +15,10 @@ ...@@ -15,11 +15,10 @@
*/ */
package com.google.android.exoplayer.text; package com.google.android.exoplayer.text;
import java.io.IOException; import com.google.android.exoplayer.ParserException;
import java.io.InputStream;
/** /**
* Parses {@link Subtitle}s from {@link InputStream}s. * Parses {@link Subtitle}s from a byte array.
*/ */
public interface SubtitleParser { public interface SubtitleParser {
...@@ -32,12 +31,14 @@ public interface SubtitleParser { ...@@ -32,12 +31,14 @@ public interface SubtitleParser {
public boolean canParse(String mimeType); 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. * @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;
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer.text; package com.google.android.exoplayer.text;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
...@@ -25,9 +26,7 @@ import android.os.Handler; ...@@ -25,9 +26,7 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
/** /**
* Wraps a {@link SubtitleParser}, exposing an interface similar to {@link MediaCodec} for * Wraps a {@link SubtitleParser}, exposing an interface similar to {@link MediaCodec} for
...@@ -165,12 +164,11 @@ import java.io.InputStream; ...@@ -165,12 +164,11 @@ import java.io.InputStream;
private void handleSample(long sampleTimeUs, SampleHolder holder) { private void handleSample(long sampleTimeUs, SampleHolder holder) {
Subtitle parsedSubtitle = null; Subtitle parsedSubtitle = null;
IOException error = null; ParserException error = null;
RuntimeException runtimeError = null; RuntimeException runtimeError = null;
try { try {
InputStream inputStream = new ByteArrayInputStream(holder.data.array(), 0, holder.size); parsedSubtitle = parser.parse(holder.data.array(), 0, holder.size);
parsedSubtitle = parser.parse(inputStream); } catch (ParserException e) {
} catch (IOException e) {
error = e; error = e;
} catch (RuntimeException e) { } catch (RuntimeException e) {
runtimeError = e; runtimeError = e;
......
...@@ -22,8 +22,6 @@ import com.google.android.exoplayer.util.MimeTypes; ...@@ -22,8 +22,6 @@ import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.ParsableByteArray; import com.google.android.exoplayer.util.ParsableByteArray;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -38,7 +36,6 @@ public final class Mp4WebvttParser implements SubtitleParser { ...@@ -38,7 +36,6 @@ public final class Mp4WebvttParser implements SubtitleParser {
private static final int TYPE_payl = Util.getIntegerCodeForString("payl"); private static final int TYPE_payl = Util.getIntegerCodeForString("payl");
private final ParsableByteArray sampleData; private final ParsableByteArray sampleData;
private byte[] inputBytesBuffer;
public Mp4WebvttParser() { public Mp4WebvttParser() {
sampleData = new ParsableByteArray(); sampleData = new ParsableByteArray();
...@@ -50,15 +47,11 @@ public final class Mp4WebvttParser implements SubtitleParser { ...@@ -50,15 +47,11 @@ public final class Mp4WebvttParser implements SubtitleParser {
} }
@Override @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: // 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. // first 4 bytes size and then 4 bytes type.
int inputStreamByteCount = inputStream.available(); sampleData.reset(bytes, offset + length);
if (inputBytesBuffer == null || inputBytesBuffer.length < inputStreamByteCount) { sampleData.setPosition(offset);
inputBytesBuffer = new byte[inputStreamByteCount];
}
inputStream.read(inputBytesBuffer, 0, inputStreamByteCount);
sampleData.reset(inputBytesBuffer, inputStreamByteCount);
List<Cue> resultingCueList = new ArrayList<>(); List<Cue> resultingCueList = new ArrayList<>();
while (sampleData.bytesLeft() > 0) { while (sampleData.bytesLeft() > 0) {
if (sampleData.bytesLeft() < BOX_HEADER_SIZE) { if (sampleData.bytesLeft() < BOX_HEADER_SIZE) {
...@@ -76,7 +69,7 @@ public final class Mp4WebvttParser implements SubtitleParser { ...@@ -76,7 +69,7 @@ public final class Mp4WebvttParser implements SubtitleParser {
return new Mp4WebvttSubtitle(resultingCueList); return new Mp4WebvttSubtitle(resultingCueList);
} }
private static Cue parseVttCueBox(ParsableByteArray sampleData) throws IOException { private static Cue parseVttCueBox(ParsableByteArray sampleData) throws ParserException {
while (sampleData.bytesLeft() > 0) { while (sampleData.bytesLeft() > 0) {
if (sampleData.bytesLeft() < BOX_HEADER_SIZE) { if (sampleData.bytesLeft() < BOX_HEADER_SIZE) {
throw new ParserException("Incomplete vtt cue box header found."); throw new ParserException("Incomplete vtt cue box header found.");
......
...@@ -15,21 +15,17 @@ ...@@ -15,21 +15,17 @@
*/ */
package com.google.android.exoplayer.text.subrip; 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.Cue;
import com.google.android.exoplayer.text.SubtitleParser; import com.google.android.exoplayer.text.SubtitleParser;
import com.google.android.exoplayer.util.LongArray; import com.google.android.exoplayer.util.LongArray;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.ParsableByteArray;
import android.text.Html; import android.text.Html;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; 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.ArrayList;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
...@@ -57,14 +53,15 @@ public final class SubripParser implements SubtitleParser { ...@@ -57,14 +53,15 @@ public final class SubripParser implements SubtitleParser {
} }
@Override @Override
public SubripSubtitle parse(InputStream inputStream) throws IOException { public SubripSubtitle parse(byte[] bytes, int offset, int length) {
ArrayList<Cue> cues = new ArrayList<>(); ArrayList<Cue> cues = new ArrayList<>();
LongArray cueTimesUs = new LongArray(); 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; boolean haveEndTimecode;
String currentLine; String currentLine;
while ((currentLine = reader.readLine()) != null) { while ((currentLine = subripData.readLine()) != null) {
if (currentLine.length() == 0) { if (currentLine.length() == 0) {
// Skip blank lines. // Skip blank lines.
continue; continue;
...@@ -80,7 +77,7 @@ public final class SubripParser implements SubtitleParser { ...@@ -80,7 +77,7 @@ public final class SubripParser implements SubtitleParser {
// Read and parse the timing line. // Read and parse the timing line.
haveEndTimecode = false; haveEndTimecode = false;
currentLine = reader.readLine(); currentLine = subripData.readLine();
Matcher matcher = SUBRIP_TIMING_LINE.matcher(currentLine); Matcher matcher = SUBRIP_TIMING_LINE.matcher(currentLine);
if (matcher.find()) { if (matcher.find()) {
cueTimesUs.add(parseTimecode(matcher.group(1))); cueTimesUs.add(parseTimecode(matcher.group(1)));
...@@ -96,7 +93,7 @@ public final class SubripParser implements SubtitleParser { ...@@ -96,7 +93,7 @@ public final class SubripParser implements SubtitleParser {
// Read and parse the text. // Read and parse the text.
textBuilder.setLength(0); textBuilder.setLength(0);
while (!TextUtils.isEmpty(currentLine = reader.readLine())) { while (!TextUtils.isEmpty(currentLine = subripData.readLine())) {
if (textBuilder.length() > 0) { if (textBuilder.length() > 0) {
textBuilder.append("<br>"); textBuilder.append("<br>");
} }
......
...@@ -17,7 +17,6 @@ package com.google.android.exoplayer.text.ttml; ...@@ -17,7 +17,6 @@ package com.google.android.exoplayer.text.ttml;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.ParserException; 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.text.SubtitleParser;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.ParserUtil; import com.google.android.exoplayer.util.ParserUtil;
...@@ -30,8 +29,8 @@ import org.xmlpull.v1.XmlPullParser; ...@@ -30,8 +29,8 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlPullParserFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map; import java.util.Map;
...@@ -97,10 +96,11 @@ public final class TtmlParser implements SubtitleParser { ...@@ -97,10 +96,11 @@ public final class TtmlParser implements SubtitleParser {
} }
@Override @Override
public Subtitle parse(InputStream inputStream) throws IOException { public TtmlSubtitle parse(byte[] bytes, int offset, int length) throws ParserException {
try { try {
XmlPullParser xmlParser = xmlParserFactory.newPullParser(); XmlPullParser xmlParser = xmlParserFactory.newPullParser();
Map<String, TtmlStyle> globalStyles = new HashMap<>(); Map<String, TtmlStyle> globalStyles = new HashMap<>();
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, offset, length);
xmlParser.setInput(inputStream, null); xmlParser.setInput(inputStream, null);
TtmlSubtitle ttmlSubtitle = null; TtmlSubtitle ttmlSubtitle = null;
LinkedList<TtmlNode> nodeStack = new LinkedList<>(); LinkedList<TtmlNode> nodeStack = new LinkedList<>();
...@@ -150,6 +150,8 @@ public final class TtmlParser implements SubtitleParser { ...@@ -150,6 +150,8 @@ public final class TtmlParser implements SubtitleParser {
return ttmlSubtitle; return ttmlSubtitle;
} catch (XmlPullParserException xppe) { } catch (XmlPullParserException xppe) {
throw new ParserException("Unable to parse source", xppe); throw new ParserException("Unable to parse source", xppe);
} catch (IOException e) {
throw new IllegalStateException("Unexpected error when reading input.", e);
} }
} }
......
...@@ -20,10 +20,6 @@ import com.google.android.exoplayer.text.Subtitle; ...@@ -20,10 +20,6 @@ import com.google.android.exoplayer.text.Subtitle;
import com.google.android.exoplayer.text.SubtitleParser; import com.google.android.exoplayer.text.SubtitleParser;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
/** /**
* A {@link SubtitleParser} for tx3g. * A {@link SubtitleParser} for tx3g.
* <p> * <p>
...@@ -37,9 +33,8 @@ public final class Tx3gParser implements SubtitleParser { ...@@ -37,9 +33,8 @@ public final class Tx3gParser implements SubtitleParser {
} }
@Override @Override
public Subtitle parse(InputStream inputStream) throws IOException { public Subtitle parse(byte[] bytes, int offset, int length) {
DataInputStream dataInputStream = new DataInputStream(inputStream); String cueText = new String(bytes, offset, length);
String cueText = dataInputStream.readUTF();
return new Tx3gSubtitle(new Cue(cueText)); return new Tx3gSubtitle(new Cue(cueText));
} }
......
...@@ -15,19 +15,16 @@ ...@@ -15,19 +15,16 @@
*/ */
package com.google.android.exoplayer.text.webvtt; 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.Cue;
import com.google.android.exoplayer.text.SubtitleParser; import com.google.android.exoplayer.text.SubtitleParser;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.ParsableByteArray;
import android.text.Layout.Alignment; import android.text.Layout.Alignment;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; 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.ArrayList;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
...@@ -59,8 +56,9 @@ public final class WebvttParser implements SubtitleParser { ...@@ -59,8 +56,9 @@ public final class WebvttParser implements SubtitleParser {
} }
@Override @Override
public final WebvttSubtitle parse(InputStream inputStream) throws IOException { public final WebvttSubtitle parse(byte[] bytes, int offset, int length) throws ParserException {
BufferedReader webvttData = new BufferedReader(new InputStreamReader(inputStream, C.UTF8_NAME)); ParsableByteArray webvttData = new ParsableByteArray(bytes, offset + length);
webvttData.setPosition(offset);
// Validate the first line of the header, and skip the remainder. // Validate the first line of the header, and skip the remainder.
WebvttParserUtil.validateWebvttHeaderLine(webvttData); WebvttParserUtil.validateWebvttHeaderLine(webvttData);
......
...@@ -16,9 +16,8 @@ ...@@ -16,9 +16,8 @@
package com.google.android.exoplayer.text.webvtt; package com.google.android.exoplayer.text.webvtt;
import com.google.android.exoplayer.ParserException; 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.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
...@@ -38,9 +37,8 @@ public final class WebvttParserUtil { ...@@ -38,9 +37,8 @@ public final class WebvttParserUtil {
* *
* @param input The input from which the line should be read. * @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 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(); String line = input.readLine();
if (line == null || !HEADER.matcher(line).matches()) { if (line == null || !HEADER.matcher(line).matches()) {
throw new ParserException("Expected WEBVTT. Got " + line); throw new ParserException("Expected WEBVTT. Got " + line);
...@@ -51,12 +49,11 @@ public final class WebvttParserUtil { ...@@ -51,12 +49,11 @@ public final class WebvttParserUtil {
* Reads lines up to and including the next WebVTT cue header. * Reads lines up to and including the next WebVTT cue header.
* *
* @param input The input from which lines should be read. * @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 * @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, * 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. * 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; String line;
while ((line = input.readLine()) != null) { while ((line = input.readLine()) != null) {
if (COMMENT.matcher(line).matches()) { if (COMMENT.matcher(line).matches()) {
......
...@@ -306,4 +306,38 @@ public final class ParsableByteArray { ...@@ -306,4 +306,38 @@ public final class ParsableByteArray {
return result; 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;
}
} }
...@@ -47,4 +47,5 @@ public final class ParserUtil { ...@@ -47,4 +47,5 @@ public final class ParserUtil {
public static String removeNamespacePrefix(String attributeName) { public static String removeNamespacePrefix(String attributeName) {
return attributeName.replaceFirst("^.*:", ""); return attributeName.replaceFirst("^.*:", "");
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment