mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Skip negative SubRip timecodes
Issue: #2145 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=140868079
This commit is contained in:
parent
e0649db513
commit
bbbd61e319
4 changed files with 36 additions and 20 deletions
|
|
@ -0,0 +1,12 @@
|
||||||
|
1
|
||||||
|
-0:00:04,567 --> -0:00:03,456
|
||||||
|
This is the first subtitle.
|
||||||
|
|
||||||
|
2
|
||||||
|
-00:00:02,345 --> 00:00:01,234
|
||||||
|
This is the second subtitle.
|
||||||
|
Second subtitle with second line.
|
||||||
|
|
||||||
|
3
|
||||||
|
00:00:04,567 --> 00:00:08,901
|
||||||
|
This is the third subtitle.
|
||||||
|
|
@ -30,6 +30,7 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
|
||||||
private static final String TYPICAL_EXTRA_BLANK_LINE = "subrip/typical_extra_blank_line";
|
private static final String TYPICAL_EXTRA_BLANK_LINE = "subrip/typical_extra_blank_line";
|
||||||
private static final String TYPICAL_MISSING_TIMECODE = "subrip/typical_missing_timecode";
|
private static final String TYPICAL_MISSING_TIMECODE = "subrip/typical_missing_timecode";
|
||||||
private static final String TYPICAL_MISSING_SEQUENCE = "subrip/typical_missing_sequence";
|
private static final String TYPICAL_MISSING_SEQUENCE = "subrip/typical_missing_sequence";
|
||||||
|
private static final String TYPICAL_NEGATIVE_TIMESTAMPS = "subrip/typical_negative_timestamps";
|
||||||
private static final String NO_END_TIMECODES_FILE = "subrip/no_end_timecodes";
|
private static final String NO_END_TIMECODES_FILE = "subrip/no_end_timecodes";
|
||||||
|
|
||||||
public void testDecodeEmpty() throws IOException {
|
public void testDecodeEmpty() throws IOException {
|
||||||
|
|
@ -91,6 +92,15 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
|
||||||
assertTypicalCue3(subtitle, 2);
|
assertTypicalCue3(subtitle, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testDecodeTypicalNegativeTimestamps() throws IOException {
|
||||||
|
// Parsing should succeed, parsing the third cue only.
|
||||||
|
SubripDecoder decoder = new SubripDecoder();
|
||||||
|
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_NEGATIVE_TIMESTAMPS);
|
||||||
|
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
|
||||||
|
assertEquals(2, subtitle.getEventTimeCount());
|
||||||
|
assertTypicalCue3(subtitle, 0);
|
||||||
|
}
|
||||||
|
|
||||||
public void testDecodeNoEndTimecodes() throws IOException {
|
public void testDecodeNoEndTimecodes() throws IOException {
|
||||||
SubripDecoder decoder = new SubripDecoder();
|
SubripDecoder decoder = new SubripDecoder();
|
||||||
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), NO_END_TIMECODES_FILE);
|
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), NO_END_TIMECODES_FILE);
|
||||||
|
|
|
||||||
|
|
@ -548,7 +548,7 @@ public final class Cea608Decoder extends CeaDecoder {
|
||||||
private static boolean isPreambleAddressCode(byte cc1, byte cc2) {
|
private static boolean isPreambleAddressCode(byte cc1, byte cc2) {
|
||||||
// cc1 - 0|0|0|1|C|X|X|X
|
// cc1 - 0|0|0|1|C|X|X|X
|
||||||
// cc2 - 0|1|X|X|X|X|X|X
|
// cc2 - 0|1|X|X|X|X|X|X
|
||||||
return ((cc1 & 0xF0) == 0x10) && ((cc2 & 0xC0) == 0x80);
|
return ((cc1 & 0xF0) == 0x10) && ((cc2 & 0xC0) == 0x40);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isTabCtrlCode(byte cc1, byte cc2) {
|
private static boolean isTabCtrlCode(byte cc1, byte cc2) {
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
|
||||||
|
|
||||||
private static final String TAG = "SubripDecoder";
|
private static final String TAG = "SubripDecoder";
|
||||||
|
|
||||||
private static final Pattern SUBRIP_TIMING_LINE = Pattern.compile("(\\S*)\\s*-->\\s*(\\S*)");
|
private static final String SUBRIP_TIMECODE = "(?:(\\d+):)?(\\d+):(\\d+),(\\d+)";
|
||||||
private static final Pattern SUBRIP_TIMESTAMP =
|
private static final Pattern SUBRIP_TIMING_LINE =
|
||||||
Pattern.compile("(?:(\\d+):)?(\\d+):(\\d+),(\\d+)");
|
Pattern.compile("\\s*(" + SUBRIP_TIMECODE + ")\\s*-->\\s*(" + SUBRIP_TIMECODE + ")?\\s*");
|
||||||
|
|
||||||
private final StringBuilder textBuilder;
|
private final StringBuilder textBuilder;
|
||||||
|
|
||||||
|
|
@ -50,7 +50,6 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
|
||||||
ArrayList<Cue> cues = new ArrayList<>();
|
ArrayList<Cue> cues = new ArrayList<>();
|
||||||
LongArray cueTimesUs = new LongArray();
|
LongArray cueTimesUs = new LongArray();
|
||||||
ParsableByteArray subripData = new ParsableByteArray(bytes, length);
|
ParsableByteArray subripData = new ParsableByteArray(bytes, length);
|
||||||
boolean haveEndTimecode;
|
|
||||||
String currentLine;
|
String currentLine;
|
||||||
|
|
||||||
while ((currentLine = subripData.readLine()) != null) {
|
while ((currentLine = subripData.readLine()) != null) {
|
||||||
|
|
@ -68,15 +67,14 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read and parse the timing line.
|
// Read and parse the timing line.
|
||||||
haveEndTimecode = false;
|
boolean haveEndTimecode = false;
|
||||||
currentLine = subripData.readLine();
|
currentLine = subripData.readLine();
|
||||||
Matcher matcher = SUBRIP_TIMING_LINE.matcher(currentLine);
|
Matcher matcher = SUBRIP_TIMING_LINE.matcher(currentLine);
|
||||||
if (matcher.find()) {
|
if (matcher.matches()) {
|
||||||
cueTimesUs.add(parseTimecode(matcher.group(1)));
|
cueTimesUs.add(parseTimecode(matcher, 1));
|
||||||
String endTimecode = matcher.group(2);
|
if (!TextUtils.isEmpty(matcher.group(6))) {
|
||||||
if (!TextUtils.isEmpty(endTimecode)) {
|
|
||||||
haveEndTimecode = true;
|
haveEndTimecode = true;
|
||||||
cueTimesUs.add(parseTimecode(matcher.group(2)));
|
cueTimesUs.add(parseTimecode(matcher, 6));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Skipping invalid timing: " + currentLine);
|
Log.w(TAG, "Skipping invalid timing: " + currentLine);
|
||||||
|
|
@ -105,15 +103,11 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
|
||||||
return new SubripSubtitle(cuesArray, cueTimesUsArray);
|
return new SubripSubtitle(cuesArray, cueTimesUsArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long parseTimecode(String s) throws NumberFormatException {
|
private static long parseTimecode(Matcher matcher, int groupOffset) {
|
||||||
Matcher matcher = SUBRIP_TIMESTAMP.matcher(s);
|
long timestampMs = Long.parseLong(matcher.group(groupOffset + 1)) * 60 * 60 * 1000;
|
||||||
if (!matcher.matches()) {
|
timestampMs += Long.parseLong(matcher.group(groupOffset + 2)) * 60 * 1000;
|
||||||
throw new NumberFormatException("has invalid format");
|
timestampMs += Long.parseLong(matcher.group(groupOffset + 3)) * 1000;
|
||||||
}
|
timestampMs += Long.parseLong(matcher.group(groupOffset + 4));
|
||||||
long timestampMs = Long.parseLong(matcher.group(1)) * 60 * 60 * 1000;
|
|
||||||
timestampMs += Long.parseLong(matcher.group(2)) * 60 * 1000;
|
|
||||||
timestampMs += Long.parseLong(matcher.group(3)) * 1000;
|
|
||||||
timestampMs += Long.parseLong(matcher.group(4));
|
|
||||||
return timestampMs * 1000;
|
return timestampMs * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue