mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Remove custom VTT logic.
This commit is contained in:
parent
dbaeecc4da
commit
02c7525ff7
10 changed files with 32 additions and 131 deletions
|
|
@ -1,6 +1,4 @@
|
||||||
EXO-HEADER=OFFSET:-5000000
|
|
||||||
WEBVTT
|
WEBVTT
|
||||||
X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000
|
|
||||||
|
|
||||||
00:00.000 --> 00:01.234
|
00:00.000 --> 00:01.234
|
||||||
This is the first subtitle.
|
This is the first subtitle.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
WEBVTT # This comment is allowed
|
WEBVTT # This comment is allowed
|
||||||
X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000
|
|
||||||
|
|
||||||
00:00.000 --> 00:01.234
|
00:00.000 --> 00:01.234
|
||||||
This is the first subtitle.
|
This is the first subtitle.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
WEBVTT
|
WEBVTT
|
||||||
X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000
|
|
||||||
|
|
||||||
1
|
1
|
||||||
00:00.000 --> 00:01.234
|
00:00.000 --> 00:01.234
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
WEBVTT
|
WEBVTT
|
||||||
X-TIMESTAMP-MAP=LOCAL:00:00.000,MPEGTS:450000
|
|
||||||
|
|
||||||
00:00.000 --> 00:01.234
|
00:00.000 --> 00:01.234
|
||||||
This is the <i>first</i> subtitle.
|
This is the <i>first</i> subtitle.
|
||||||
|
|
|
||||||
|
|
@ -53,21 +53,20 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||||
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
||||||
|
|
||||||
// test start time and event count
|
// test start time and event count
|
||||||
long startTimeUs = 5000000;
|
assertEquals(0, subtitle.getStartTime());
|
||||||
assertEquals(startTimeUs, subtitle.getStartTime());
|
|
||||||
assertEquals(4, subtitle.getEventTimeCount());
|
assertEquals(4, subtitle.getEventTimeCount());
|
||||||
|
|
||||||
// test first cue
|
// test first cue
|
||||||
assertEquals(startTimeUs, subtitle.getEventTime(0));
|
assertEquals(0, subtitle.getEventTime(0));
|
||||||
assertEquals("This is the first subtitle.",
|
assertEquals("This is the first subtitle.",
|
||||||
subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString());
|
subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString());
|
||||||
assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1));
|
assertEquals(1234000, subtitle.getEventTime(1));
|
||||||
|
|
||||||
// test second cue
|
// test second cue
|
||||||
assertEquals(startTimeUs + 2345000, subtitle.getEventTime(2));
|
assertEquals(2345000, subtitle.getEventTime(2));
|
||||||
assertEquals("This is the second subtitle.",
|
assertEquals("This is the second subtitle.",
|
||||||
subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString());
|
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 {
|
public void testParseTypicalWithIdsWebvttFile() throws IOException {
|
||||||
|
|
@ -78,21 +77,20 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||||
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
||||||
|
|
||||||
// test start time and event count
|
// test start time and event count
|
||||||
long startTimeUs = 5000000;
|
assertEquals(0, subtitle.getStartTime());
|
||||||
assertEquals(startTimeUs, subtitle.getStartTime());
|
|
||||||
assertEquals(4, subtitle.getEventTimeCount());
|
assertEquals(4, subtitle.getEventTimeCount());
|
||||||
|
|
||||||
// test first cue
|
// test first cue
|
||||||
assertEquals(startTimeUs, subtitle.getEventTime(0));
|
assertEquals(0, subtitle.getEventTime(0));
|
||||||
assertEquals("This is the first subtitle.",
|
assertEquals("This is the first subtitle.",
|
||||||
subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString());
|
subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString());
|
||||||
assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1));
|
assertEquals(1234000, subtitle.getEventTime(1));
|
||||||
|
|
||||||
// test second cue
|
// test second cue
|
||||||
assertEquals(startTimeUs + 2345000, subtitle.getEventTime(2));
|
assertEquals(2345000, subtitle.getEventTime(2));
|
||||||
assertEquals("This is the second subtitle.",
|
assertEquals("This is the second subtitle.",
|
||||||
subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString());
|
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 {
|
public void testParseTypicalWithTagsWebvttFile() throws IOException {
|
||||||
|
|
@ -103,33 +101,32 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||||
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
||||||
|
|
||||||
// test start time and event count
|
// test start time and event count
|
||||||
long startTimeUs = 5000000;
|
assertEquals(0, subtitle.getStartTime());
|
||||||
assertEquals(startTimeUs, subtitle.getStartTime());
|
|
||||||
assertEquals(8, subtitle.getEventTimeCount());
|
assertEquals(8, subtitle.getEventTimeCount());
|
||||||
|
|
||||||
// test first cue
|
// test first cue
|
||||||
assertEquals(startTimeUs, subtitle.getEventTime(0));
|
assertEquals(0, subtitle.getEventTime(0));
|
||||||
assertEquals("This is the first subtitle.",
|
assertEquals("This is the first subtitle.",
|
||||||
subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString());
|
subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString());
|
||||||
assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1));
|
assertEquals(1234000, subtitle.getEventTime(1));
|
||||||
|
|
||||||
// test second cue
|
// test second cue
|
||||||
assertEquals(startTimeUs + 2345000, subtitle.getEventTime(2));
|
assertEquals(2345000, subtitle.getEventTime(2));
|
||||||
assertEquals("This is the second subtitle.",
|
assertEquals("This is the second subtitle.",
|
||||||
subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString());
|
subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString());
|
||||||
assertEquals(startTimeUs + 3456000, subtitle.getEventTime(3));
|
assertEquals(3456000, subtitle.getEventTime(3));
|
||||||
|
|
||||||
// test third cue
|
// test third cue
|
||||||
assertEquals(startTimeUs + 4000000, subtitle.getEventTime(4));
|
assertEquals(4000000, subtitle.getEventTime(4));
|
||||||
assertEquals("This is the third subtitle.",
|
assertEquals("This is the third subtitle.",
|
||||||
subtitle.getCues(subtitle.getEventTime(4)).get(0).text.toString());
|
subtitle.getCues(subtitle.getEventTime(4)).get(0).text.toString());
|
||||||
assertEquals(startTimeUs + 5000000, subtitle.getEventTime(5));
|
assertEquals(5000000, subtitle.getEventTime(5));
|
||||||
|
|
||||||
// test fourth cue
|
// test fourth cue
|
||||||
assertEquals(startTimeUs + 6000000, subtitle.getEventTime(6));
|
assertEquals(6000000, subtitle.getEventTime(6));
|
||||||
assertEquals("This is the <fourth> &subtitle.",
|
assertEquals("This is the <fourth> &subtitle.",
|
||||||
subtitle.getCues(subtitle.getEventTime(6)).get(0).text.toString());
|
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 {
|
public void testParseLiveTypicalWebvttFile() throws IOException {
|
||||||
|
|
|
||||||
|
|
@ -90,18 +90,6 @@ public final class C {
|
||||||
*/
|
*/
|
||||||
public static final int RESULT_END_OF_INPUT = -1;
|
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() {}
|
private C() {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ public final class SingleSampleChunkSource implements ChunkSource {
|
||||||
|
|
||||||
private SingleSampleMediaChunk initChunk() {
|
private SingleSampleMediaChunk initChunk() {
|
||||||
return new SingleSampleMediaChunk(dataSource, dataSpec, Chunk.TRIGGER_UNSPECIFIED, format, 0,
|
return new SingleSampleMediaChunk(dataSource, dataSpec, Chunk.TRIGGER_UNSPECIFIED, format, 0,
|
||||||
durationUs, 0, true, mediaFormat, null, null);
|
durationUs, 0, true, mediaFormat, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import com.google.android.exoplayer.MediaFormat;
|
||||||
import com.google.android.exoplayer.drm.DrmInitData;
|
import com.google.android.exoplayer.drm.DrmInitData;
|
||||||
import com.google.android.exoplayer.upstream.DataSource;
|
import com.google.android.exoplayer.upstream.DataSource;
|
||||||
import com.google.android.exoplayer.upstream.DataSpec;
|
import com.google.android.exoplayer.upstream.DataSpec;
|
||||||
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.IOException;
|
||||||
|
|
@ -32,9 +31,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
|
||||||
|
|
||||||
private final MediaFormat sampleFormat;
|
private final MediaFormat sampleFormat;
|
||||||
private final DrmInitData sampleDrmInitData;
|
private final DrmInitData sampleDrmInitData;
|
||||||
private final byte[] headerData;
|
|
||||||
|
|
||||||
private boolean writtenHeader;
|
|
||||||
|
|
||||||
private volatile int bytesLoaded;
|
private volatile int bytesLoaded;
|
||||||
private volatile boolean loadCanceled;
|
private volatile boolean loadCanceled;
|
||||||
|
|
@ -51,18 +47,14 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
|
||||||
* @param sampleFormat The format of the sample.
|
* @param sampleFormat The format of the sample.
|
||||||
* @param sampleDrmInitData The {@link DrmInitData} for the sample. Null if the sample is not drm
|
* @param sampleDrmInitData The {@link DrmInitData} for the sample. Null if the sample is not drm
|
||||||
* protected.
|
* 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,
|
public SingleSampleMediaChunk(DataSource dataSource, DataSpec dataSpec, int trigger,
|
||||||
Format format, long startTimeUs, long endTimeUs, int chunkIndex, boolean isLastChunk,
|
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,
|
super(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs, chunkIndex, isLastChunk,
|
||||||
true);
|
true);
|
||||||
this.sampleFormat = sampleFormat;
|
this.sampleFormat = sampleFormat;
|
||||||
this.sampleDrmInitData = sampleDrmInitData;
|
this.sampleDrmInitData = sampleDrmInitData;
|
||||||
this.headerData = headerData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -95,13 +87,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
|
||||||
@SuppressWarnings("NonAtomicVolatileUpdate")
|
@SuppressWarnings("NonAtomicVolatileUpdate")
|
||||||
@Override
|
@Override
|
||||||
public void load() throws IOException, InterruptedException {
|
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);
|
DataSpec loadDataSpec = Util.getRemainderDataSpec(dataSpec, bytesLoaded);
|
||||||
try {
|
try {
|
||||||
// Create and open the input.
|
// Create and open the input.
|
||||||
|
|
@ -113,9 +98,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
|
||||||
result = getOutput().sampleData(dataSource, Integer.MAX_VALUE, true);
|
result = getOutput().sampleData(dataSource, Integer.MAX_VALUE, true);
|
||||||
}
|
}
|
||||||
int sampleSize = bytesLoaded;
|
int sampleSize = bytesLoaded;
|
||||||
if (headerData != null) {
|
|
||||||
sampleSize += headerData.length;
|
|
||||||
}
|
|
||||||
getOutput().sampleMetadata(startTimeUs, C.SAMPLE_FLAG_SYNC, sampleSize, 0, null);
|
getOutput().sampleMetadata(startTimeUs, C.SAMPLE_FLAG_SYNC, sampleSize, 0, null);
|
||||||
} finally {
|
} finally {
|
||||||
dataSource.close();
|
dataSource.close();
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
package com.google.android.exoplayer.dash;
|
package com.google.android.exoplayer.dash;
|
||||||
|
|
||||||
import com.google.android.exoplayer.BehindLiveWindowException;
|
import com.google.android.exoplayer.BehindLiveWindowException;
|
||||||
import com.google.android.exoplayer.C;
|
|
||||||
import com.google.android.exoplayer.MediaFormat;
|
import com.google.android.exoplayer.MediaFormat;
|
||||||
import com.google.android.exoplayer.TimeRange;
|
import com.google.android.exoplayer.TimeRange;
|
||||||
import com.google.android.exoplayer.TrackInfo;
|
import com.google.android.exoplayer.TrackInfo;
|
||||||
|
|
@ -104,7 +103,6 @@ public class DashChunkSource implements ChunkSource {
|
||||||
private final FormatEvaluator formatEvaluator;
|
private final FormatEvaluator formatEvaluator;
|
||||||
private final Evaluation evaluation;
|
private final Evaluation evaluation;
|
||||||
private final Clock systemClock;
|
private final Clock systemClock;
|
||||||
private final StringBuilder headerBuilder;
|
|
||||||
private final long liveEdgeLatencyUs;
|
private final long liveEdgeLatencyUs;
|
||||||
private final long elapsedRealtimeOffsetUs;
|
private final long elapsedRealtimeOffsetUs;
|
||||||
private final int maxWidth;
|
private final int maxWidth;
|
||||||
|
|
@ -258,7 +256,6 @@ public class DashChunkSource implements ChunkSource {
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
this.evaluation = new Evaluation();
|
this.evaluation = new Evaluation();
|
||||||
this.headerBuilder = new StringBuilder();
|
|
||||||
this.seekRangeValues = new long[2];
|
this.seekRangeValues = new long[2];
|
||||||
|
|
||||||
drmInitData = getDrmInitData(currentManifest, adaptationSetIndex);
|
drmInitData = getDrmInitData(currentManifest, adaptationSetIndex);
|
||||||
|
|
@ -645,19 +642,9 @@ public class DashChunkSource implements ChunkSource {
|
||||||
long sampleOffsetUs = representation.periodStartMs * 1000
|
long sampleOffsetUs = representation.periodStartMs * 1000
|
||||||
- representation.presentationTimeOffsetUs;
|
- representation.presentationTimeOffsetUs;
|
||||||
if (representation.format.mimeType.equals(MimeTypes.TEXT_VTT)) {
|
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,
|
return new SingleSampleMediaChunk(dataSource, dataSpec, Chunk.TRIGGER_INITIAL,
|
||||||
representation.format, startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment,
|
representation.format, startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment,
|
||||||
MediaFormat.createTextFormat(MimeTypes.TEXT_VTT, representation.format.language), null,
|
MediaFormat.createTextFormat(MimeTypes.TEXT_VTT, representation.format.language), null);
|
||||||
representationHolder.vttHeader);
|
|
||||||
} else {
|
} else {
|
||||||
return new ContainerMediaChunk(dataSource, dataSpec, trigger, representation.format,
|
return new ContainerMediaChunk(dataSource, dataSpec, trigger, representation.format,
|
||||||
startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment, sampleOffsetUs,
|
startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment, sampleOffsetUs,
|
||||||
|
|
@ -741,8 +728,6 @@ public class DashChunkSource implements ChunkSource {
|
||||||
public MediaFormat format;
|
public MediaFormat format;
|
||||||
|
|
||||||
public int segmentNumShift;
|
public int segmentNumShift;
|
||||||
public long vttHeaderOffsetUs;
|
|
||||||
public byte[] vttHeader;
|
|
||||||
|
|
||||||
public RepresentationHolder(Representation representation,
|
public RepresentationHolder(Representation representation,
|
||||||
ChunkExtractorWrapper extractorWrapper) {
|
ChunkExtractorWrapper extractorWrapper) {
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,10 @@ import java.util.regex.Pattern;
|
||||||
* <p>
|
* <p>
|
||||||
* @see <a href="http://dev.w3.org/html5/webvtt">WebVTT specification</a>
|
* @see <a href="http://dev.w3.org/html5/webvtt">WebVTT specification</a>
|
||||||
*/
|
*/
|
||||||
public class WebvttParser implements SubtitleParser {
|
public final class WebvttParser implements SubtitleParser {
|
||||||
|
|
||||||
private static final String TAG = "WebvttParser";
|
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 String WEBVTT_FILE_HEADER_STRING = "^\uFEFF?WEBVTT((\\u0020|\u0009).*)?$";
|
||||||
private static final Pattern WEBVTT_FILE_HEADER =
|
private static final Pattern WEBVTT_FILE_HEADER =
|
||||||
Pattern.compile(WEBVTT_FILE_HEADER_STRING);
|
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 String WEBVTT_CUE_SETTING_STRING = "\\S*:\\S*";
|
||||||
private static final Pattern WEBVTT_CUE_SETTING = Pattern.compile(WEBVTT_CUE_SETTING_STRING);
|
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 static final String NON_NUMERIC_STRING = ".*[^0-9].*";
|
||||||
|
|
||||||
private final StringBuilder textBuilder;
|
private final StringBuilder textBuilder;
|
||||||
|
|
@ -94,33 +88,13 @@ public class WebvttParser implements SubtitleParser {
|
||||||
public final WebvttSubtitle parse(InputStream inputStream, String inputEncoding, long startTimeUs)
|
public final WebvttSubtitle parse(InputStream inputStream, String inputEncoding, long startTimeUs)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
ArrayList<WebvttCue> subtitles = new ArrayList<>();
|
ArrayList<WebvttCue> subtitles = new ArrayList<>();
|
||||||
long mediaTimestampUs = startTimeUs;
|
|
||||||
long mediaTimestampOffsetUs = 0;
|
|
||||||
|
|
||||||
BufferedReader webvttData = new BufferedReader(new InputStreamReader(inputStream, C.UTF8_NAME));
|
BufferedReader webvttData = new BufferedReader(new InputStreamReader(inputStream, C.UTF8_NAME));
|
||||||
String line;
|
String line;
|
||||||
|
|
||||||
// file should start with "WEBVTT" on the first line or "EXO-HEADER"
|
// file should start with "WEBVTT"
|
||||||
line = webvttData.readLine();
|
line = webvttData.readLine();
|
||||||
if (line == null) {
|
if (line == null || !WEBVTT_FILE_HEADER.matcher(line).matches()) {
|
||||||
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()) {
|
|
||||||
throw new ParserException("Expected WEBVTT. Got " + line);
|
throw new ParserException("Expected WEBVTT. Got " + line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,21 +109,11 @@ public class WebvttParser implements SubtitleParser {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matcher matcher = WEBVTT_METADATA_HEADER.matcher(line);
|
if (strictParsing) {
|
||||||
if (!matcher.find()) {
|
Matcher matcher = WEBVTT_METADATA_HEADER.matcher(line);
|
||||||
handleNoncompliantLine(line);
|
if (!matcher.find()) {
|
||||||
}
|
throw new ParserException("Unexpected line: " + 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;
|
|
||||||
}
|
}
|
||||||
mediaTimestampUs = getAdjustedStartTime(mediaTimestampUs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,7 +142,7 @@ public class WebvttParser implements SubtitleParser {
|
||||||
if (!matcher.find()) {
|
if (!matcher.find()) {
|
||||||
throw new ParserException("Expected cue start time: " + line);
|
throw new ParserException("Expected cue start time: " + line);
|
||||||
} else {
|
} else {
|
||||||
startTime = parseTimestampUs(matcher.group()) + mediaTimestampUs;
|
startTime = parseTimestampUs(matcher.group()) + startTimeUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse end timestamp
|
// parse end timestamp
|
||||||
|
|
@ -187,7 +151,7 @@ public class WebvttParser implements SubtitleParser {
|
||||||
throw new ParserException("Expected cue end time: " + line);
|
throw new ParserException("Expected cue end time: " + line);
|
||||||
} else {
|
} else {
|
||||||
endTimeString = matcher.group();
|
endTimeString = matcher.group();
|
||||||
endTime = parseTimestampUs(endTimeString) + mediaTimestampUs;
|
endTime = parseTimestampUs(endTimeString) + startTimeUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the (optional) cue setting list
|
// parse the (optional) cue setting list
|
||||||
|
|
@ -249,7 +213,7 @@ public class WebvttParser implements SubtitleParser {
|
||||||
subtitles.add(cue);
|
subtitles.add(cue);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WebvttSubtitle(subtitles, mediaTimestampUs);
|
return new WebvttSubtitle(subtitles, startTimeUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -257,16 +221,6 @@ public class WebvttParser implements SubtitleParser {
|
||||||
return MimeTypes.TEXT_VTT.equals(mimeType);
|
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 {
|
private static int parseIntPercentage(String s) throws NumberFormatException {
|
||||||
if (!s.endsWith("%")) {
|
if (!s.endsWith("%")) {
|
||||||
throw new NumberFormatException(s + " doesn't end with '%'");
|
throw new NumberFormatException(s + " doesn't end with '%'");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue