diff --git a/library/src/androidTest/assets/webvtt/live_typical b/library/src/androidTest/assets/webvtt/live_typical index 9f4864b690..ec791f357a 100644 --- a/library/src/androidTest/assets/webvtt/live_typical +++ b/library/src/androidTest/assets/webvtt/live_typical @@ -1,6 +1,4 @@ -EXO-HEADER=OFFSET:-5000000 WEBVTT -X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000 00:00.000 --> 00:01.234 This is the first subtitle. diff --git a/library/src/androidTest/assets/webvtt/typical b/library/src/androidTest/assets/webvtt/typical index 7bd3ca9198..d4298da516 100644 --- a/library/src/androidTest/assets/webvtt/typical +++ b/library/src/androidTest/assets/webvtt/typical @@ -1,5 +1,4 @@ WEBVTT # This comment is allowed -X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000 00:00.000 --> 00:01.234 This is the first subtitle. diff --git a/library/src/androidTest/assets/webvtt/typical_with_identifiers b/library/src/androidTest/assets/webvtt/typical_with_identifiers index e2c5df065b..f6ac990e99 100644 --- a/library/src/androidTest/assets/webvtt/typical_with_identifiers +++ b/library/src/androidTest/assets/webvtt/typical_with_identifiers @@ -1,5 +1,4 @@ WEBVTT -X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000 1 00:00.000 --> 00:01.234 diff --git a/library/src/androidTest/assets/webvtt/typical_with_tags b/library/src/androidTest/assets/webvtt/typical_with_tags index aecf1cb2b7..56610f84fc 100644 --- a/library/src/androidTest/assets/webvtt/typical_with_tags +++ b/library/src/androidTest/assets/webvtt/typical_with_tags @@ -1,5 +1,4 @@ WEBVTT -X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000 00:00.000 --> 00:01.234 This is the first subtitle. 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 e8bb58ed9f..f8e8daf43d 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 @@ -53,21 +53,20 @@ public class WebvttParserTest extends InstrumentationTestCase { WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0); // test start time and event count - long startTimeUs = 5000000; - assertEquals(startTimeUs, subtitle.getStartTime()); + assertEquals(0, subtitle.getStartTime()); assertEquals(4, subtitle.getEventTimeCount()); // test first cue - assertEquals(startTimeUs, subtitle.getEventTime(0)); + assertEquals(0, subtitle.getEventTime(0)); assertEquals("This is the first subtitle.", subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString()); - assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1)); + assertEquals(1234000, subtitle.getEventTime(1)); // test second cue - assertEquals(startTimeUs + 2345000, subtitle.getEventTime(2)); + assertEquals(2345000, subtitle.getEventTime(2)); assertEquals("This is the second subtitle.", subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString()); - assertEquals(startTimeUs + 3456000, subtitle.getEventTime(3)); + assertEquals(3456000, subtitle.getEventTime(3)); } public void testParseTypicalWithIdsWebvttFile() throws IOException { @@ -78,21 +77,20 @@ public class WebvttParserTest extends InstrumentationTestCase { WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0); // test start time and event count - long startTimeUs = 5000000; - assertEquals(startTimeUs, subtitle.getStartTime()); + assertEquals(0, subtitle.getStartTime()); assertEquals(4, subtitle.getEventTimeCount()); // test first cue - assertEquals(startTimeUs, subtitle.getEventTime(0)); + assertEquals(0, subtitle.getEventTime(0)); assertEquals("This is the first subtitle.", subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString()); - assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1)); + assertEquals(1234000, subtitle.getEventTime(1)); // test second cue - assertEquals(startTimeUs + 2345000, subtitle.getEventTime(2)); + assertEquals(2345000, subtitle.getEventTime(2)); assertEquals("This is the second subtitle.", subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString()); - assertEquals(startTimeUs + 3456000, subtitle.getEventTime(3)); + assertEquals(3456000, subtitle.getEventTime(3)); } public void testParseTypicalWithTagsWebvttFile() throws IOException { @@ -103,33 +101,32 @@ public class WebvttParserTest extends InstrumentationTestCase { WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0); // test start time and event count - long startTimeUs = 5000000; - assertEquals(startTimeUs, subtitle.getStartTime()); + assertEquals(0, subtitle.getStartTime()); assertEquals(8, subtitle.getEventTimeCount()); // test first cue - assertEquals(startTimeUs, subtitle.getEventTime(0)); + assertEquals(0, subtitle.getEventTime(0)); assertEquals("This is the first subtitle.", subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString()); - assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1)); + assertEquals(1234000, subtitle.getEventTime(1)); // test second cue - assertEquals(startTimeUs + 2345000, subtitle.getEventTime(2)); + assertEquals(2345000, subtitle.getEventTime(2)); assertEquals("This is the second subtitle.", subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString()); - assertEquals(startTimeUs + 3456000, subtitle.getEventTime(3)); + assertEquals(3456000, subtitle.getEventTime(3)); // test third cue - assertEquals(startTimeUs + 4000000, subtitle.getEventTime(4)); + assertEquals(4000000, subtitle.getEventTime(4)); assertEquals("This is the third subtitle.", subtitle.getCues(subtitle.getEventTime(4)).get(0).text.toString()); - assertEquals(startTimeUs + 5000000, subtitle.getEventTime(5)); + assertEquals(5000000, subtitle.getEventTime(5)); // test fourth cue - assertEquals(startTimeUs + 6000000, subtitle.getEventTime(6)); + assertEquals(6000000, subtitle.getEventTime(6)); assertEquals("This is the &subtitle.", subtitle.getCues(subtitle.getEventTime(6)).get(0).text.toString()); - assertEquals(startTimeUs + 7000000, subtitle.getEventTime(7)); + assertEquals(7000000, subtitle.getEventTime(7)); } public void testParseLiveTypicalWebvttFile() throws IOException { diff --git a/library/src/main/java/com/google/android/exoplayer/C.java b/library/src/main/java/com/google/android/exoplayer/C.java index af6e619a6d..145e151099 100644 --- a/library/src/main/java/com/google/android/exoplayer/C.java +++ b/library/src/main/java/com/google/android/exoplayer/C.java @@ -90,18 +90,6 @@ public final class C { */ public static final int RESULT_END_OF_INPUT = -1; - /** - * A prefix for custom ExoPlayer WebVTT headers. - */ - public static final String WEBVTT_EXO_HEADER = "EXO-HEADER"; - - /** - * An element of a custom ExoPlayer WebVTT header. An {@code WEBVTT_OFFSET + value} element can - * be added to a custom ExoPlayer WebVTT header to specify an offset time (in microseconds) that - * should be added to the embedded MPEGTS value. - */ - public static final String WEBVTT_EXO_HEADER_OFFSET = "OFFSET:"; - private C() {} } diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java index 12f7689099..1068e1699a 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleChunkSource.java @@ -109,7 +109,7 @@ public final class SingleSampleChunkSource implements ChunkSource { private SingleSampleMediaChunk initChunk() { return new SingleSampleMediaChunk(dataSource, dataSpec, Chunk.TRIGGER_UNSPECIFIED, format, 0, - durationUs, 0, true, mediaFormat, null, null); + durationUs, 0, true, mediaFormat, null); } } diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java index de2c7067cc..064638580b 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/SingleSampleMediaChunk.java @@ -20,7 +20,6 @@ import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.drm.DrmInitData; import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSpec; -import com.google.android.exoplayer.util.ParsableByteArray; import com.google.android.exoplayer.util.Util; import java.io.IOException; @@ -32,9 +31,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { private final MediaFormat sampleFormat; private final DrmInitData sampleDrmInitData; - private final byte[] headerData; - - private boolean writtenHeader; private volatile int bytesLoaded; private volatile boolean loadCanceled; @@ -51,18 +47,14 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { * @param sampleFormat The format of the sample. * @param sampleDrmInitData The {@link DrmInitData} for the sample. Null if the sample is not drm * protected. - * @param headerData Custom header data for the sample. May be null. If set, the header data is - * prepended to the sample data. It is not reflected in the values returned by - * {@link #bytesLoaded()}. */ public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format, long startTimeUs, long endTimeUs, int chunkIndex, boolean isLastChunk, - MediaFormat sampleFormat, DrmInitData sampleDrmInitData, byte[] headerData) { + MediaFormat sampleFormat, DrmInitData sampleDrmInitData) { super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex, isLastChunk, true); this.sampleFormat = sampleFormat; this.sampleDrmInitData = sampleDrmInitData; - this.headerData = headerData; } @Override @@ -95,13 +87,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { @SuppressWarnings("NonAtomicVolatileUpdate") @Override public void load() throws IOException, InterruptedException { - if (!writtenHeader) { - if (headerData != null) { - getOutput().sampleData(new ParsableByteArray(headerData), headerData.length); - } - writtenHeader = true; - } - DataSpec loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded); try { // Create and open the input. @@ -113,9 +98,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk { result = getOutput().sampleData(dataSource, Integer.MAX_VALUE, true); } int sampleSize = bytesLoaded; - if (headerData != null) { - sampleSize += headerData.length; - } getOutput().sampleMetadata(startTimeUs, C.SAMPLE_FLAG_SYNC, sampleSize, 0, null); } finally { dataSource.close(); diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java index 815f4c02dd..eb1b096947 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer.dash; import com.google.android.exoplayer.BehindLiveWindowException; -import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.TimeRange; import com.google.android.exoplayer.TrackInfo; @@ -104,7 +103,6 @@ public class DashChunkSource implements ChunkSource { private final FormatEvaluator formatEvaluator; private final Evaluation evaluation; private final Clock systemClock; - private final StringBuilder headerBuilder; private final long liveEdgeLatencyUs; private final long elapsedRealtimeOffsetUs; private final int maxWidth; @@ -258,7 +256,6 @@ public class DashChunkSource implements ChunkSource { this.eventHandler = eventHandler; this.eventListener = eventListener; this.evaluation = new Evaluation(); - this.headerBuilder = new StringBuilder(); this.seekRangeValues = new long[2]; drmInitData = getDrmInitData(currentManifest, adaptationSetIndex); @@ -645,19 +642,9 @@ public class DashChunkSource implements ChunkSource { long sampleOffsetUs = representation.periodStartMs * 1000 - representation.presentationTimeOffsetUs; if (representation.format.mimeType.equals(MimeTypes.TEXT_VTT)) { - if (representationHolder.vttHeaderOffsetUs != sampleOffsetUs) { - // Update the VTT header. - headerBuilder.setLength(0); - headerBuilder.append(C.WEBVTT_EXO_HEADER).append("=") - .append(C.WEBVTT_EXO_HEADER_OFFSET).append(sampleOffsetUs) - .append("\n"); - representationHolder.vttHeader = headerBuilder.toString().getBytes(); - representationHolder.vttHeaderOffsetUs = sampleOffsetUs; - } return new SingleSampleMediaChunk(dataSource, dataSpec, Chunk.TRIGGER_INITIAL, representation.format, startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment, - MediaFormat.createTextFormat(MimeTypes.TEXT_VTT, representation.format.language), null, - representationHolder.vttHeader); + MediaFormat.createTextFormat(MimeTypes.TEXT_VTT, representation.format.language), null); } else { return new ContainerMediaChunk(dataSource, dataSpec, trigger, representation.format, startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment, sampleOffsetUs, @@ -741,8 +728,6 @@ public class DashChunkSource implements ChunkSource { public MediaFormat format; public int segmentNumShift; - public long vttHeaderOffsetUs; - public byte[] vttHeader; public RepresentationHolder(Representation representation, ChunkExtractorWrapper extractorWrapper) { 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 bebf643e82..091f8150f6 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 @@ -38,12 +38,10 @@ import java.util.regex.Pattern; *

* @see WebVTT specification */ -public class WebvttParser implements SubtitleParser { +public final class WebvttParser implements SubtitleParser { private static final String TAG = "WebvttParser"; - private static final long SAMPLING_RATE = 90; - private static final String WEBVTT_FILE_HEADER_STRING = "^\uFEFF?WEBVTT((\\u0020|\u0009).*)?$"; private static final Pattern WEBVTT_FILE_HEADER = Pattern.compile(WEBVTT_FILE_HEADER_STRING); @@ -62,10 +60,6 @@ public class WebvttParser implements SubtitleParser { private static final String WEBVTT_CUE_SETTING_STRING = "\\S*:\\S*"; private static final Pattern WEBVTT_CUE_SETTING = Pattern.compile(WEBVTT_CUE_SETTING_STRING); - private static final Pattern MEDIA_TIMESTAMP_OFFSET = - Pattern.compile(C.WEBVTT_EXO_HEADER_OFFSET + "\\-?\\d+"); - private static final Pattern MEDIA_TIMESTAMP = Pattern.compile("MPEGTS:\\d+"); - private static final String NON_NUMERIC_STRING = ".*[^0-9].*"; private final StringBuilder textBuilder; @@ -94,33 +88,13 @@ public class WebvttParser implements SubtitleParser { public final WebvttSubtitle parse(InputStream inputStream, String inputEncoding, long startTimeUs) throws IOException { ArrayList subtitles = new ArrayList<>(); - long mediaTimestampUs = startTimeUs; - long mediaTimestampOffsetUs = 0; BufferedReader webvttData = new BufferedReader(new InputStreamReader(inputStream, C.UTF8_NAME)); String line; - // file should start with "WEBVTT" on the first line or "EXO-HEADER" + // file should start with "WEBVTT" line = webvttData.readLine(); - if (line == null) { - throw new ParserException("Expected WEBVTT or EXO-HEADER. Got null"); - } - - if (line.startsWith(C.WEBVTT_EXO_HEADER)) { - // parse the timestamp offset, if present - Matcher matcher = MEDIA_TIMESTAMP_OFFSET.matcher(line); - if (matcher.find()) { - mediaTimestampOffsetUs = Long.parseLong(matcher.group().substring(7)); - } - - // read the next line, which should now be WEBVTT - line = webvttData.readLine(); - if (line == null) { - throw new ParserException("Expected WEBVTT. Got null"); - } - } - - if (!WEBVTT_FILE_HEADER.matcher(line).matches()) { + if (line == null || !WEBVTT_FILE_HEADER.matcher(line).matches()) { throw new ParserException("Expected WEBVTT. Got " + line); } @@ -135,21 +109,11 @@ public class WebvttParser implements SubtitleParser { break; } - Matcher matcher = WEBVTT_METADATA_HEADER.matcher(line); - if (!matcher.find()) { - handleNoncompliantLine(line); - } - - if (line.startsWith("X-TIMESTAMP-MAP")) { - // parse the media timestamp - Matcher timestampMatcher = MEDIA_TIMESTAMP.matcher(line); - if (!timestampMatcher.find()) { - throw new ParserException("X-TIMESTAMP-MAP doesn't contain media timestamp: " + line); - } else { - mediaTimestampUs = (Long.parseLong(timestampMatcher.group().substring(7)) * 1000) - / SAMPLING_RATE + mediaTimestampOffsetUs; + if (strictParsing) { + Matcher matcher = WEBVTT_METADATA_HEADER.matcher(line); + if (!matcher.find()) { + throw new ParserException("Unexpected line: " + line); } - mediaTimestampUs = getAdjustedStartTime(mediaTimestampUs); } } @@ -178,7 +142,7 @@ public class WebvttParser implements SubtitleParser { if (!matcher.find()) { throw new ParserException("Expected cue start time: " + line); } else { - startTime = parseTimestampUs(matcher.group()) + mediaTimestampUs; + startTime = parseTimestampUs(matcher.group()) + startTimeUs; } // parse end timestamp @@ -187,7 +151,7 @@ public class WebvttParser implements SubtitleParser { throw new ParserException("Expected cue end time: " + line); } else { endTimeString = matcher.group(); - endTime = parseTimestampUs(endTimeString) + mediaTimestampUs; + endTime = parseTimestampUs(endTimeString) + startTimeUs; } // parse the (optional) cue setting list @@ -249,7 +213,7 @@ public class WebvttParser implements SubtitleParser { subtitles.add(cue); } - return new WebvttSubtitle(subtitles, mediaTimestampUs); + return new WebvttSubtitle(subtitles, startTimeUs); } @Override @@ -257,16 +221,6 @@ public class WebvttParser implements SubtitleParser { return MimeTypes.TEXT_VTT.equals(mimeType); } - protected long getAdjustedStartTime(long startTimeUs) { - return startTimeUs; - } - - protected void handleNoncompliantLine(String line) throws ParserException { - if (strictParsing) { - throw new ParserException("Unexpected line: " + line); - } - } - private static int parseIntPercentage(String s) throws NumberFormatException { if (!s.endsWith("%")) { throw new NumberFormatException(s + " doesn't end with '%'");