mirror of
https://github.com/androidx/media.git
synced 2025-05-07 15:40:37 +08:00
Further cleanup subtitle implementations.
This commit is contained in:
parent
bdd1968abe
commit
02c978e16c
@ -94,7 +94,7 @@ public final class C {
|
||||
/**
|
||||
* 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 subtracted from the embedded MPEGTS value.
|
||||
* should be added to the embedded MPEGTS value.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
|
@ -132,7 +132,7 @@ public final class VideoFormatSelectorUtil {
|
||||
}
|
||||
}
|
||||
|
||||
return Util.toIntArray(selectedIndexList);
|
||||
return Util.toArray(selectedIndexList);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,8 +165,8 @@ public class DashChunkSource implements ChunkSource {
|
||||
* note that the value sets an upper bound on the length of media that the player can buffer.
|
||||
* Hence a small value may increase the probability of rebuffering and playback failures.
|
||||
* @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between
|
||||
* server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified
|
||||
* as the server's unix time minus the local elapsed time. It unknown, set to 0.
|
||||
* server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified
|
||||
* as the server's unix time minus the local elapsed time. It unknown, set to 0.
|
||||
*/
|
||||
public DashChunkSource(ManifestFetcher<MediaPresentationDescription> manifestFetcher,
|
||||
int adaptationSetIndex, int[] representationIndices, DataSource dataSource,
|
||||
@ -491,23 +491,23 @@ public class DashChunkSource implements ChunkSource {
|
||||
DataSpec dataSpec = new DataSpec(segmentUri.getUri(), segmentUri.start, segmentUri.length,
|
||||
representation.getCacheKey());
|
||||
|
||||
long presentationTimeOffsetUs = representation.presentationTimeOffsetUs;
|
||||
long sampleOffsetUs = -1 * representation.presentationTimeOffsetUs;
|
||||
if (representation.format.mimeType.equals(MimeTypes.TEXT_VTT)) {
|
||||
if (representationHolder.vttHeaderOffsetUs != presentationTimeOffsetUs) {
|
||||
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(presentationTimeOffsetUs)
|
||||
.append(C.WEBVTT_EXO_HEADER_OFFSET).append(sampleOffsetUs)
|
||||
.append("\n");
|
||||
representationHolder.vttHeader = headerBuilder.toString().getBytes();
|
||||
representationHolder.vttHeaderOffsetUs = presentationTimeOffsetUs;
|
||||
representationHolder.vttHeaderOffsetUs = sampleOffsetUs;
|
||||
}
|
||||
return new SingleSampleMediaChunk(dataSource, dataSpec, Chunk.TRIGGER_INITIAL,
|
||||
representation.format, startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment,
|
||||
MediaFormat.createTextFormat(MimeTypes.TEXT_VTT), null, representationHolder.vttHeader);
|
||||
} else {
|
||||
return new ContainerMediaChunk(dataSource, dataSpec, trigger, representation.format,
|
||||
startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment, 0,
|
||||
startTimeUs, endTimeUs, absoluteSegmentNum, isLastSegment, sampleOffsetUs,
|
||||
representationHolder.extractorWrapper, representationHolder.format, drmInitData, true);
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ 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.LongArray;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
@ -36,8 +36,6 @@ import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A simple SubRip parser.
|
||||
* <p/>
|
||||
* @see <a href="https://en.wikipedia.org/wiki/SubRip">Wikipedia on SubRip</a>
|
||||
*/
|
||||
public final class SubripParser implements SubtitleParser {
|
||||
|
||||
@ -53,9 +51,9 @@ public final class SubripParser implements SubtitleParser {
|
||||
|
||||
@Override
|
||||
public SubripSubtitle parse(InputStream inputStream, String inputEncoding, long startTimeUs)
|
||||
throws IOException {
|
||||
throws IOException {
|
||||
ArrayList<Cue> cues = new ArrayList<>();
|
||||
ArrayList<Long> cueTimesUs = new ArrayList<>();
|
||||
LongArray cueTimesUs = new LongArray();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, C.UTF8_NAME));
|
||||
String currentLine;
|
||||
|
||||
@ -90,12 +88,9 @@ public final class SubripParser implements SubtitleParser {
|
||||
cues.add(new Cue(text));
|
||||
}
|
||||
|
||||
reader.close();
|
||||
inputStream.close();
|
||||
|
||||
Cue[] cuesArray = new Cue[cues.size()];
|
||||
cues.toArray(cuesArray);
|
||||
long[] cueTimesUsArray = Util.toLongArray(cueTimesUs);
|
||||
long[] cueTimesUsArray = cueTimesUs.toArray();
|
||||
return new SubripSubtitle(startTimeUs, cuesArray, cueTimesUsArray);
|
||||
}
|
||||
|
||||
|
@ -35,12 +35,8 @@ public final class Tx3gParser implements SubtitleParser {
|
||||
public Subtitle parse(InputStream inputStream, String inputEncoding, long startTimeUs)
|
||||
throws IOException {
|
||||
DataInputStream dataInputStream = new DataInputStream(inputStream);
|
||||
try {
|
||||
String cueText = dataInputStream.readUTF();
|
||||
return new Tx3gSubtitle(startTimeUs, new Cue(cueText));
|
||||
} finally {
|
||||
dataInputStream.close();
|
||||
}
|
||||
String cueText = dataInputStream.readUTF();
|
||||
return new Tx3gSubtitle(startTimeUs, new Cue(cueText));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,7 +59,7 @@ public class WebvttParser implements SubtitleParser {
|
||||
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+");
|
||||
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].*";
|
||||
@ -135,7 +135,7 @@ public class WebvttParser implements SubtitleParser {
|
||||
throw new ParserException("X-TIMESTAMP-MAP doesn't contain media timestamp: " + line);
|
||||
} else {
|
||||
mediaTimestampUs = (Long.parseLong(timestampMatcher.group().substring(7)) * 1000)
|
||||
/ SAMPLING_RATE - mediaTimestampOffsetUs;
|
||||
/ SAMPLING_RATE + mediaTimestampOffsetUs;
|
||||
}
|
||||
mediaTimestampUs = getAdjustedStartTime(mediaTimestampUs);
|
||||
}
|
||||
@ -237,10 +237,7 @@ public class WebvttParser implements SubtitleParser {
|
||||
subtitles.add(cue);
|
||||
}
|
||||
|
||||
webvttData.close();
|
||||
inputStream.close();
|
||||
WebvttSubtitle subtitle = new WebvttSubtitle(subtitles, mediaTimestampUs);
|
||||
return subtitle;
|
||||
return new WebvttSubtitle(subtitles, mediaTimestampUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,7 +20,7 @@ import java.util.Arrays;
|
||||
/**
|
||||
* An append-only, auto-growing {@code long[]}.
|
||||
*/
|
||||
public class LongArray {
|
||||
public final class LongArray {
|
||||
|
||||
private static final int DEFAULT_INITIAL_CAPACITY = 32;
|
||||
|
||||
@ -74,4 +74,13 @@ public class LongArray {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the current values into a newly allocated primitive array.
|
||||
*
|
||||
* @return The primitive array containing the copied values.
|
||||
*/
|
||||
public long[] toArray() {
|
||||
return Arrays.copyOf(values, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ public final class Util {
|
||||
* @param list A list of integers.
|
||||
* @return The list in array form, or null if the input list was null.
|
||||
*/
|
||||
public static int[] toIntArray(List<Integer> list) {
|
||||
public static int[] toArray(List<Integer> list) {
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
@ -471,24 +471,6 @@ public final class Util {
|
||||
return intArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of longs to a primitive array.
|
||||
*
|
||||
* @param list A list of longs.
|
||||
* @return The list in array form, or null if the input list was null.
|
||||
*/
|
||||
public static long[] toLongArray(List<Long> list) {
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
int length = list.size();
|
||||
long[] longArray = new long[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
longArray[i] = list.get(i);
|
||||
}
|
||||
return longArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* On platform API levels 19 and 20, okhttp's implementation of {@link InputStream#close} can
|
||||
* block for a long time if the stream has a lot of data remaining. Call this method before
|
||||
|
9
library/src/test/assets/webvtt/live_typical
Normal file
9
library/src/test/assets/webvtt/live_typical
Normal file
@ -0,0 +1,9 @@
|
||||
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.
|
||||
|
||||
00:02.345 --> 00:03.456
|
||||
This is the second subtitle.
|
@ -25,7 +25,7 @@ import java.io.InputStream;
|
||||
/**
|
||||
* Unit test for {@link SubripParser}.
|
||||
*/
|
||||
public class SubripParserTest extends InstrumentationTestCase {
|
||||
public final class SubripParserTest extends InstrumentationTestCase {
|
||||
|
||||
private static final String TYPICAL_SUBRIP_FILE = "subrip/typical";
|
||||
private static final String EMPTY_SUBRIP_FILE = "subrip/empty";
|
||||
|
@ -30,6 +30,7 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
private static final String TYPICAL_WEBVTT_FILE = "webvtt/typical";
|
||||
private static final String TYPICAL_WITH_IDS_WEBVTT_FILE = "webvtt/typical_with_identifiers";
|
||||
private static final String TYPICAL_WITH_TAGS_WEBVTT_FILE = "webvtt/typical_with_tags";
|
||||
private static final String LIVE_TYPICAL_WEBVTT_FILE = "webvtt/live_typical";
|
||||
private static final String EMPTY_WEBVTT_FILE = "webvtt/empty";
|
||||
|
||||
public void testParseNullWebvttFile() throws IOException {
|
||||
@ -131,4 +132,28 @@ public class WebvttParserTest extends InstrumentationTestCase {
|
||||
assertEquals(startTimeUs + 7000000, subtitle.getEventTime(7));
|
||||
}
|
||||
|
||||
public void testParseLiveTypicalWebvttFile() throws IOException {
|
||||
WebvttParser parser = new WebvttParser();
|
||||
InputStream inputStream =
|
||||
getInstrumentation().getContext().getResources().getAssets().open(LIVE_TYPICAL_WEBVTT_FILE);
|
||||
WebvttSubtitle subtitle = parser.parse(inputStream, C.UTF8_NAME, 0);
|
||||
|
||||
// test start time and event count
|
||||
long startTimeUs = 0;
|
||||
assertEquals(startTimeUs, subtitle.getStartTime());
|
||||
assertEquals(4, subtitle.getEventTimeCount());
|
||||
|
||||
// test first cue
|
||||
assertEquals(startTimeUs, subtitle.getEventTime(0));
|
||||
assertEquals("This is the first subtitle.",
|
||||
subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString());
|
||||
assertEquals(startTimeUs + 1234000, subtitle.getEventTime(1));
|
||||
|
||||
// test second cue
|
||||
assertEquals(startTimeUs + 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user