Commit 0c20462b by ibaker Committed by Oliver Woodman

Allow missing hours in SubRip (.srt) subtitle timecodes

Add a test for this case, and extend the existing tests to ensure the
hour is parsed when it's present.

issue:#7122
PiperOrigin-RevId: 302472213
parent 92c4088a
...@@ -75,6 +75,8 @@ ...@@ -75,6 +75,8 @@
([#6885](https://github.com/google/ExoPlayer/issues/6885)). ([#6885](https://github.com/google/ExoPlayer/issues/6885)).
* Parse `tts:ruby` and `tts:rubyPosition` properties in TTML subtitles * Parse `tts:ruby` and `tts:rubyPosition` properties in TTML subtitles
(rendering is coming later). (rendering is coming later).
* Allow missing hours in SubRip (.srt) timecodes
([#7122](https://github.com/google/ExoPlayer/issues/7122)).
* DRM: * DRM:
* Add support for attaching DRM sessions to clear content in the demo app. * Add support for attaching DRM sessions to clear content in the demo app.
* Remove `DrmSessionManager` references from all renderers. * Remove `DrmSessionManager` references from all renderers.
......
...@@ -41,10 +41,12 @@ public final class SubripDecoder extends SimpleSubtitleDecoder { ...@@ -41,10 +41,12 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
private static final String TAG = "SubripDecoder"; private static final String TAG = "SubripDecoder";
// Some SRT files don't include hours in the timecode, so we use an optional group.
private static final String SUBRIP_TIMECODE = "(?:(\\d+):)?(\\d+):(\\d+),(\\d+)"; private static final String SUBRIP_TIMECODE = "(?:(\\d+):)?(\\d+):(\\d+),(\\d+)";
private static final Pattern SUBRIP_TIMING_LINE = private static final Pattern SUBRIP_TIMING_LINE =
Pattern.compile("\\s*(" + SUBRIP_TIMECODE + ")\\s*-->\\s*(" + SUBRIP_TIMECODE + ")\\s*"); Pattern.compile("\\s*(" + SUBRIP_TIMECODE + ")\\s*-->\\s*(" + SUBRIP_TIMECODE + ")\\s*");
// NOTE: Android Studio's suggestion to simplify '\\}' is incorrect [internal: b/144480183].
private static final Pattern SUBRIP_TAG_PATTERN = Pattern.compile("\\{\\\\.*?\\}"); private static final Pattern SUBRIP_TAG_PATTERN = Pattern.compile("\\{\\\\.*?\\}");
private static final String SUBRIP_ALIGNMENT_TAG = "\\{\\\\an[1-9]\\}"; private static final String SUBRIP_ALIGNMENT_TAG = "\\{\\\\an[1-9]\\}";
...@@ -229,7 +231,8 @@ public final class SubripDecoder extends SimpleSubtitleDecoder { ...@@ -229,7 +231,8 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
} }
private static long parseTimecode(Matcher matcher, int groupOffset) { private static long parseTimecode(Matcher matcher, int groupOffset) {
long timestampMs = Long.parseLong(matcher.group(groupOffset + 1)) * 60 * 60 * 1000; @Nullable String hours = matcher.group(groupOffset + 1);
long timestampMs = hours != null ? Long.parseLong(hours) * 60 * 60 * 1000 : 0;
timestampMs += Long.parseLong(matcher.group(groupOffset + 2)) * 60 * 1000; timestampMs += Long.parseLong(matcher.group(groupOffset + 2)) * 60 * 1000;
timestampMs += Long.parseLong(matcher.group(groupOffset + 3)) * 1000; timestampMs += Long.parseLong(matcher.group(groupOffset + 3)) * 1000;
timestampMs += Long.parseLong(matcher.group(groupOffset + 4)); timestampMs += Long.parseLong(matcher.group(groupOffset + 4));
......
...@@ -39,6 +39,7 @@ public final class SubripDecoderTest { ...@@ -39,6 +39,7 @@ public final class SubripDecoderTest {
private static final String TYPICAL_NEGATIVE_TIMESTAMPS = "subrip/typical_negative_timestamps"; private static final String TYPICAL_NEGATIVE_TIMESTAMPS = "subrip/typical_negative_timestamps";
private static final String TYPICAL_UNEXPECTED_END = "subrip/typical_unexpected_end"; private static final String TYPICAL_UNEXPECTED_END = "subrip/typical_unexpected_end";
private static final String TYPICAL_WITH_TAGS = "subrip/typical_with_tags"; private static final String TYPICAL_WITH_TAGS = "subrip/typical_with_tags";
private static final String TYPICAL_NO_HOURS = "subrip/typical_no_hours";
@Test @Test
public void decodeEmpty() throws IOException { public void decodeEmpty() throws IOException {
...@@ -151,9 +152,14 @@ public final class SubripDecoderTest { ...@@ -151,9 +152,14 @@ public final class SubripDecoderTest {
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_WITH_TAGS); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_WITH_TAGS);
Subtitle subtitle = decoder.decode(bytes, bytes.length, false); Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertTypicalCue1(subtitle, 0); assertThat(subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString())
assertTypicalCue2(subtitle, 2); .isEqualTo("This is the first subtitle.");
assertTypicalCue3(subtitle, 4);
assertThat(subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString())
.isEqualTo("This is the second subtitle.\nSecond subtitle with second line.");
assertThat(subtitle.getCues(subtitle.getEventTime(4)).get(0).text.toString())
.isEqualTo("This is the third subtitle.");
assertThat(subtitle.getCues(subtitle.getEventTime(6)).get(0).text.toString()) assertThat(subtitle.getCues(subtitle.getEventTime(6)).get(0).text.toString())
.isEqualTo("This { \\an2} is not a valid tag due to the space after the opening bracket."); .isEqualTo("This { \\an2} is not a valid tag due to the space after the opening bracket.");
...@@ -172,6 +178,19 @@ public final class SubripDecoderTest { ...@@ -172,6 +178,19 @@ public final class SubripDecoderTest {
assertAlignmentCue(subtitle, 26, Cue.ANCHOR_TYPE_START, Cue.ANCHOR_TYPE_END); // {/an9} assertAlignmentCue(subtitle, 26, Cue.ANCHOR_TYPE_START, Cue.ANCHOR_TYPE_END); // {/an9}
} }
@Test
public void decodeTypicalNoHours() throws IOException {
SubripDecoder decoder = new SubripDecoder();
byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_NO_HOURS);
Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertThat(subtitle.getEventTimeCount()).isEqualTo(6);
assertTypicalCue1(subtitle, 0);
assertTypicalCue2(subtitle, 2);
assertTypicalCue3(subtitle, 4);
}
private static void assertTypicalCue1(Subtitle subtitle, int eventIndex) { private static void assertTypicalCue1(Subtitle subtitle, int eventIndex) {
assertThat(subtitle.getEventTime(eventIndex)).isEqualTo(0); assertThat(subtitle.getEventTime(eventIndex)).isEqualTo(0);
assertThat(subtitle.getCues(subtitle.getEventTime(eventIndex)).get(0).text.toString()) assertThat(subtitle.getCues(subtitle.getEventTime(eventIndex)).get(0).text.toString())
...@@ -187,10 +206,12 @@ public final class SubripDecoderTest { ...@@ -187,10 +206,12 @@ public final class SubripDecoderTest {
} }
private static void assertTypicalCue3(Subtitle subtitle, int eventIndex) { private static void assertTypicalCue3(Subtitle subtitle, int eventIndex) {
assertThat(subtitle.getEventTime(eventIndex)).isEqualTo(4567000); long expectedStartTimeUs = (((2L * 60L * 60L) + 4L) * 1000L + 567L) * 1000L;
assertThat(subtitle.getEventTime(eventIndex)).isEqualTo(expectedStartTimeUs);
assertThat(subtitle.getCues(subtitle.getEventTime(eventIndex)).get(0).text.toString()) assertThat(subtitle.getCues(subtitle.getEventTime(eventIndex)).get(0).text.toString())
.isEqualTo("This is the third subtitle."); .isEqualTo("This is the third subtitle.");
assertThat(subtitle.getEventTime(eventIndex + 1)).isEqualTo(8901000); long expectedEndTimeUs = (((2L * 60L * 60L) + 8L) * 1000L + 901L) * 1000L;
assertThat(subtitle.getEventTime(eventIndex + 1)).isEqualTo(expectedEndTimeUs);
} }
private static void assertAlignmentCue( private static void assertAlignmentCue(
......
...@@ -8,5 +8,5 @@ This is the second subtitle. ...@@ -8,5 +8,5 @@ This is the second subtitle.
Second subtitle with second line. Second subtitle with second line.
3 3
00:00:04,567 --> 00:00:08,901 02:00:04,567 --> 02:00:08,901
This is the third subtitle. This is the third subtitle.
...@@ -9,5 +9,5 @@ This is the second subtitle. ...@@ -9,5 +9,5 @@ This is the second subtitle.
Second subtitle with second line. Second subtitle with second line.
3 3
00:00:04,567 --> 00:00:08,901 02:00:04,567 --> 02:00:08,901
This is the third subtitle. This is the third subtitle.
...@@ -7,5 +7,5 @@ This is the second subtitle. ...@@ -7,5 +7,5 @@ This is the second subtitle.
Second subtitle with second line. Second subtitle with second line.
3 3
00:00:04,567 --> 00:00:08,901 02:00:04,567 --> 02:00:08,901
This is the third subtitle. This is the third subtitle.
...@@ -7,13 +7,13 @@ This is the second subtitle. ...@@ -7,13 +7,13 @@ This is the second subtitle.
Second subtitle with second line. Second subtitle with second line.
3 3
00:00:04,567 --> 00:00:08,901 02:00:04,567 --> 02:00:08,901
This is the third subtitle. This is the third subtitle.
4 4
--> 00:00:10,901 --> 02:00:10,901
This is the fourth subtitle. This is the fourth subtitle.
5 5
00:00:12,901 --> 02:00:12,901 -->
This is the fifth subtitle. This is the fifth subtitle.
...@@ -8,5 +8,5 @@ This is the second subtitle. ...@@ -8,5 +8,5 @@ This is the second subtitle.
Second subtitle with second line. Second subtitle with second line.
3 3
00:00:04,567 --> 00:00:08,901 02:00:04,567 --> 02:00:08,901
This is the third subtitle. This is the third subtitle.
1
00:00,000 --> 00:01,234
This is the first subtitle.
2
00:02,345 --> 00:03,456
This is the second subtitle.
Second subtitle with second line.
3
02:00:04,567 --> 02:00:08,901
This is the third subtitle.
...@@ -8,5 +8,5 @@ This is the second subtitle. ...@@ -8,5 +8,5 @@ This is the second subtitle.
Second subtitle with second line. Second subtitle with second line.
3 3
00:00:04,567 --> 00:00:08,901 02:00:04,567 --> 02:00:08,901
This is the third subtitle. This is the third subtitle.
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