diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java index 71c9b13c66..22aee60a9c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java @@ -28,8 +28,6 @@ public final class WebvttParserUtil { private static final Pattern COMMENT = Pattern.compile("^NOTE((\u0020|\u0009).*)?$"); private static final String WEBVTT_HEADER = "WEBVTT"; - private static final int WEBVTT_BOM_BE = 0xfeff; - private static final int WEBVTT_BOM_LE = 0xfffe; private WebvttParserUtil() {} @@ -53,15 +51,6 @@ public final class WebvttParserUtil { * @param input The input from which the line should be read. */ public static boolean isWebvttHeaderLine(ParsableByteArray input) { - if (input.bytesLeft() < 2) { - return false; - } - int startPosition = input.getPosition(); - int firstTwoBytes = input.readUnsignedShort(); - if (firstTwoBytes != WEBVTT_BOM_BE && firstTwoBytes != WEBVTT_BOM_LE) { - // Not the BOM, should not be discarded. - input.setPosition(startPosition); - } String line = input.readLine(); return line != null && line.startsWith(WEBVTT_HEADER); } diff --git a/library/core/src/test/assets/webvtt/with_bom b/library/core/src/test/assets/webvtt/with_bom index 6945098468..b7110ec015 100644 --- a/library/core/src/test/assets/webvtt/with_bom +++ b/library/core/src/test/assets/webvtt/with_bom @@ -1,4 +1,4 @@ -þÿWEBVTT # This comment is allowed +WEBVTT # This comment is allowed 00:00.000 --> 00:01.234 This is the first subtitle. diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/WebvttExtractor.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/WebvttExtractor.java index ddeaf39f4c..665f2e0570 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/WebvttExtractor.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/WebvttExtractor.java @@ -46,7 +46,8 @@ public final class WebvttExtractor implements Extractor { private static final Pattern LOCAL_TIMESTAMP = Pattern.compile("LOCAL:([^,]+)"); private static final Pattern MEDIA_TIMESTAMP = Pattern.compile("MPEGTS:(\\d+)"); - private static final int HEADER_MAX_LENGTH = 2 /* optional Byte Order Mark */ + 6 /* "WEBVTT" */; + private static final int HEADER_MIN_LENGTH = 6 /* "WEBVTT" */; + private static final int HEADER_MAX_LENGTH = 3 /* optional Byte Order Mark */ + HEADER_MIN_LENGTH; private final String language; private final TimestampAdjuster timestampAdjuster; @@ -68,8 +69,19 @@ public final class WebvttExtractor implements Extractor { @Override public boolean sniff(ExtractorInput input) throws IOException, InterruptedException { + // Check whether there is a header without BOM. input.peekFully( - sampleData, /* offset= */ 0, /* length= */ HEADER_MAX_LENGTH, /* allowEndOfInput= */ false); + sampleData, /* offset= */ 0, /* length= */ HEADER_MIN_LENGTH, /* allowEndOfInput= */ false); + sampleDataWrapper.reset(sampleData, HEADER_MIN_LENGTH); + if (WebvttParserUtil.isWebvttHeaderLine(sampleDataWrapper)) { + return true; + } + // The header did not match, try including the BOM. + input.peekFully( + sampleData, + /* offset= */ HEADER_MIN_LENGTH, + HEADER_MAX_LENGTH - HEADER_MIN_LENGTH, + /* allowEndOfInput= */ false); sampleDataWrapper.reset(sampleData, HEADER_MAX_LENGTH); return WebvttParserUtil.isWebvttHeaderLine(sampleDataWrapper); } diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/WebvttExtractorTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/WebvttExtractorTest.java index bbba72db02..e3e2feed8b 100644 --- a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/WebvttExtractorTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/WebvttExtractorTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.testutil.FakeExtractorInput; import com.google.android.exoplayer2.util.TimestampAdjuster; +import java.io.EOFException; import java.io.IOException; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,30 +38,37 @@ public class WebvttExtractorTest { @Test public void sniff_discardsByteOrderMark() throws IOException, InterruptedException { - byte[] data = new byte[] {(byte) 0xFE, (byte) 0xFF, 'W', 'E', 'B', 'V', 'T', 'T', '\n', ' '}; + byte[] data = + new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF, 'W', 'E', 'B', 'V', 'T', 'T', '\n', ' '}; assertThat(sniffData(data)).isTrue(); } @Test public void sniff_failsForIncorrectBom() throws IOException, InterruptedException { - byte[] data = new byte[] {(byte) 0xFE, (byte) 0xFE, 'W', 'E', 'B', 'V', 'T', 'T', '\n'}; + byte[] data = + new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBB, 'W', 'E', 'B', 'V', 'T', 'T', '\n'}; assertThat(sniffData(data)).isFalse(); } @Test public void sniff_failsForIncompleteHeader() throws IOException, InterruptedException { - byte[] data = new byte[] {(byte) 0xFE, (byte) 0xFE, 'W', 'E', 'B', 'V', 'T', '\n'}; + byte[] data = new byte[] {'W', 'E', 'B', 'V', 'T', '\n'}; assertThat(sniffData(data)).isFalse(); } @Test public void sniff_failsForIncorrectHeader() throws IOException, InterruptedException { - byte[] data = new byte[] {(byte) 0xFE, (byte) 0xFE, 'W', 'e', 'B', 'V', 'T', 'T', '\n'}; + byte[] data = + new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF, 'W', 'e', 'B', 'V', 'T', 'T', '\n'}; assertThat(sniffData(data)).isFalse(); } private static boolean sniffData(byte[] data) throws IOException, InterruptedException { ExtractorInput input = new FakeExtractorInput.Builder().setData(data).build(); - return new WebvttExtractor(/* language= */ null, new TimestampAdjuster(0)).sniff(input); + try { + return new WebvttExtractor(/* language= */ null, new TimestampAdjuster(0)).sniff(input); + } catch (EOFException e) { + return false; + } } }