mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Mp4WebvttParser
implementation - moved from Mp4WebvttDecoder
`Mp4WebvttDecoder` which used to be `SimpleSubtitleDecoder` will now be called `Mp4WebvttParser` and implement `SubtitleParser` interface. For backwards compatibility, we will have the same functionality provided by `DelegatingSubtitleDecoder` backed-up by a new `Mp4WebvttParser` instance. PiperOrigin-RevId: 547248157
This commit is contained in:
parent
c122c0ca05
commit
02b9d8d8b7
@ -28,7 +28,7 @@ import androidx.media3.extractor.text.ssa.SsaParser;
|
||||
import androidx.media3.extractor.text.subrip.SubripParser;
|
||||
import androidx.media3.extractor.text.ttml.TtmlDecoder;
|
||||
import androidx.media3.extractor.text.tx3g.Tx3gDecoder;
|
||||
import androidx.media3.extractor.text.webvtt.Mp4WebvttDecoder;
|
||||
import androidx.media3.extractor.text.webvtt.Mp4WebvttParser;
|
||||
import androidx.media3.extractor.text.webvtt.WebvttDecoder;
|
||||
|
||||
/** A factory for {@link SubtitleDecoder} instances. */
|
||||
@ -60,7 +60,7 @@ public interface SubtitleDecoderFactory {
|
||||
*
|
||||
* <ul>
|
||||
* <li>WebVTT ({@link WebvttDecoder})
|
||||
* <li>WebVTT (MP4) ({@link Mp4WebvttDecoder})
|
||||
* <li>WebVTT (MP4) ({@link Mp4WebvttParser})
|
||||
* <li>TTML ({@link TtmlDecoder})
|
||||
* <li>SubRip ({@link SubripParser})
|
||||
* <li>SSA/ASS ({@link SsaParser})
|
||||
@ -104,7 +104,8 @@ public interface SubtitleDecoderFactory {
|
||||
"DelegatingSubtitleDecoderWithSsaParser",
|
||||
new SsaParser(format.initializationData));
|
||||
case MimeTypes.APPLICATION_MP4VTT:
|
||||
return new Mp4WebvttDecoder();
|
||||
return new DelegatingSubtitleDecoder(
|
||||
"DelegatingSubtitleDecoderWithMp4WebvttParser", new Mp4WebvttParser());
|
||||
case MimeTypes.APPLICATION_TTML:
|
||||
return new TtmlDecoder();
|
||||
case MimeTypes.APPLICATION_SUBRIP:
|
||||
|
@ -13,14 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.extractor.text.webvtt;
|
||||
package androidx.media3.exoplayer.text;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.extractor.text.Subtitle;
|
||||
import androidx.media3.extractor.text.SubtitleDecoderException;
|
||||
import androidx.media3.extractor.text.webvtt.Mp4WebvttParser;
|
||||
import androidx.media3.extractor.text.webvtt.WebvttCueParser;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.truth.Expect;
|
||||
import java.util.List;
|
||||
@ -28,9 +29,9 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Unit test for {@link Mp4WebvttDecoder}. */
|
||||
/** Unit test for a {@link DelegatingSubtitleDecoder} backed by {@link Mp4WebvttParser}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class Mp4WebvttDecoderTest {
|
||||
public final class DelegatingSubtitleDecoderWithMp4WebvttParserTest {
|
||||
|
||||
private static final byte[] SINGLE_CUE_SAMPLE = {
|
||||
0x00,
|
||||
@ -161,8 +162,10 @@ public final class Mp4WebvttDecoderTest {
|
||||
// Positive tests.
|
||||
|
||||
@Test
|
||||
public void singleCueSample() throws SubtitleDecoderException {
|
||||
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
|
||||
public void singleCueSample() {
|
||||
DelegatingSubtitleDecoder decoder =
|
||||
new DelegatingSubtitleDecoder(
|
||||
"DelegatingSubtitleDecoderWithMp4WebvttParser", new Mp4WebvttParser());
|
||||
Subtitle result = decoder.decode(SINGLE_CUE_SAMPLE, SINGLE_CUE_SAMPLE.length, false);
|
||||
// Line feed must be trimmed by the decoder
|
||||
Cue expectedCue = WebvttCueParser.newCueForText("Hello World");
|
||||
@ -170,8 +173,10 @@ public final class Mp4WebvttDecoderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoCuesSample() throws SubtitleDecoderException {
|
||||
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
|
||||
public void twoCuesSample() {
|
||||
DelegatingSubtitleDecoder decoder =
|
||||
new DelegatingSubtitleDecoder(
|
||||
"DelegatingSubtitleDecoderWithMp4WebvttParser", new Mp4WebvttParser());
|
||||
Subtitle result = decoder.decode(DOUBLE_CUE_SAMPLE, DOUBLE_CUE_SAMPLE.length, false);
|
||||
Cue firstExpectedCue = WebvttCueParser.newCueForText("Hello World");
|
||||
Cue secondExpectedCue = WebvttCueParser.newCueForText("Bye Bye");
|
||||
@ -179,25 +184,24 @@ public final class Mp4WebvttDecoderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCueSample() throws SubtitleDecoderException {
|
||||
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
|
||||
public void noCueSample() {
|
||||
DelegatingSubtitleDecoder decoder =
|
||||
new DelegatingSubtitleDecoder(
|
||||
"DelegatingSubtitleDecoderWithMp4WebvttParser", new Mp4WebvttParser());
|
||||
Subtitle result = decoder.decode(NO_CUE_SAMPLE, NO_CUE_SAMPLE.length, false);
|
||||
assertThat(result.getEventTimeCount()).isEqualTo(1);
|
||||
assertThat(result.getEventTime(0)).isEqualTo(0);
|
||||
assertThat(result.getCues(0)).isEmpty();
|
||||
assertThat(result.getEventTimeCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
// Negative tests.
|
||||
|
||||
@Test
|
||||
public void sampleWithIncompleteHeader() {
|
||||
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
|
||||
try {
|
||||
decoder.decode(INCOMPLETE_HEADER_SAMPLE, INCOMPLETE_HEADER_SAMPLE.length, false);
|
||||
} catch (SubtitleDecoderException e) {
|
||||
return;
|
||||
}
|
||||
fail();
|
||||
DelegatingSubtitleDecoder decoder =
|
||||
new DelegatingSubtitleDecoder(
|
||||
"DelegatingSubtitleDecoderWithMp4WebvttParser", new Mp4WebvttParser());
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> decoder.decode(INCOMPLETE_HEADER_SAMPLE, INCOMPLETE_HEADER_SAMPLE.length, false));
|
||||
}
|
||||
|
||||
// Util methods
|
@ -15,22 +15,25 @@
|
||||
*/
|
||||
package androidx.media3.extractor.text.webvtt;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.extractor.text.SimpleSubtitleDecoder;
|
||||
import androidx.media3.extractor.text.Subtitle;
|
||||
import androidx.media3.extractor.text.SubtitleDecoderException;
|
||||
import androidx.media3.extractor.text.CuesWithTiming;
|
||||
import androidx.media3.extractor.text.SubtitleParser;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** A {@link SimpleSubtitleDecoder} for Webvtt embedded in a Mp4 container file. */
|
||||
/** A {@link SubtitleParser} for Webvtt embedded in a Mp4 container file. */
|
||||
@SuppressWarnings("ConstantField")
|
||||
@UnstableApi
|
||||
public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
|
||||
public final class Mp4WebvttParser implements SubtitleParser {
|
||||
|
||||
private static final int BOX_HEADER_SIZE = 8;
|
||||
|
||||
@ -44,43 +47,55 @@ public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
|
||||
private static final int TYPE_vttc = 0x76747463;
|
||||
|
||||
private final ParsableByteArray sampleData;
|
||||
private byte[] dataScratch = Util.EMPTY_BYTE_ARRAY;
|
||||
|
||||
public Mp4WebvttDecoder() {
|
||||
super("Mp4WebvttDecoder");
|
||||
public Mp4WebvttParser() {
|
||||
sampleData = new ParsableByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Subtitle decode(byte[] data, int length, boolean reset)
|
||||
throws SubtitleDecoderException {
|
||||
// Webvtt in Mp4 samples have boxes inside of them, so we have to do a traditional box parsing:
|
||||
// first 4 bytes size and then 4 bytes type.
|
||||
sampleData.reset(data, length);
|
||||
List<Cue> resultingCueList = new ArrayList<>();
|
||||
while (sampleData.bytesLeft() > 0) {
|
||||
if (sampleData.bytesLeft() < BOX_HEADER_SIZE) {
|
||||
throw new SubtitleDecoderException("Incomplete Mp4Webvtt Top Level box header found.");
|
||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
||||
if (offset != 0) {
|
||||
if (dataScratch.length < length) {
|
||||
dataScratch = new byte[length];
|
||||
}
|
||||
System.arraycopy(
|
||||
/* src= */ data, /* scrPos= */ offset, /* dest= */ dataScratch, /* destPos= */ 0, length);
|
||||
sampleData.reset(dataScratch, length);
|
||||
} else {
|
||||
sampleData.reset(data, length);
|
||||
}
|
||||
List<Cue> cues = new ArrayList<>();
|
||||
while (sampleData.bytesLeft() > 0) {
|
||||
// Webvtt in Mp4 samples have boxes inside of them, so we have to do a traditional box
|
||||
// parsing: first 4 bytes size and then 4 bytes type.
|
||||
checkArgument(
|
||||
sampleData.bytesLeft() >= BOX_HEADER_SIZE,
|
||||
"Incomplete Mp4Webvtt Top Level box header found.");
|
||||
int boxSize = sampleData.readInt();
|
||||
int boxType = sampleData.readInt();
|
||||
if (boxType == TYPE_vttc) {
|
||||
resultingCueList.add(parseVttCueBox(sampleData, boxSize - BOX_HEADER_SIZE));
|
||||
cues.add(parseVttCueBox(sampleData, boxSize - BOX_HEADER_SIZE));
|
||||
} else {
|
||||
// Peers of the VTTCueBox are still not supported and are skipped.
|
||||
sampleData.skipBytes(boxSize - BOX_HEADER_SIZE);
|
||||
}
|
||||
}
|
||||
return new Mp4WebvttSubtitle(resultingCueList);
|
||||
return cues.isEmpty()
|
||||
? ImmutableList.of()
|
||||
: ImmutableList.of(
|
||||
new CuesWithTiming(cues, /* startTimeUs= */ 0, /* durationUs= */ C.TIME_UNSET));
|
||||
}
|
||||
|
||||
private static Cue parseVttCueBox(ParsableByteArray sampleData, int remainingCueBoxBytes)
|
||||
throws SubtitleDecoderException {
|
||||
@Override
|
||||
public void reset() {}
|
||||
|
||||
private static Cue parseVttCueBox(ParsableByteArray sampleData, int remainingCueBoxBytes) {
|
||||
@Nullable Cue.Builder cueBuilder = null;
|
||||
@Nullable CharSequence cueText = null;
|
||||
while (remainingCueBoxBytes > 0) {
|
||||
if (remainingCueBoxBytes < BOX_HEADER_SIZE) {
|
||||
throw new SubtitleDecoderException("Incomplete vtt cue box header found.");
|
||||
}
|
||||
checkArgument(
|
||||
remainingCueBoxBytes >= BOX_HEADER_SIZE, "Incomplete vtt cue box header found.");
|
||||
int boxSize = sampleData.readInt();
|
||||
int boxType = sampleData.readInt();
|
||||
remainingCueBoxBytes -= BOX_HEADER_SIZE;
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.extractor.text.webvtt;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.extractor.text.Subtitle;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** Representation of a Webvtt subtitle embedded in a MP4 container file. */
|
||||
/* package */ final class Mp4WebvttSubtitle implements Subtitle {
|
||||
|
||||
private final List<Cue> cues;
|
||||
|
||||
public Mp4WebvttSubtitle(List<Cue> cueList) {
|
||||
cues = Collections.unmodifiableList(cueList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextEventTimeIndex(long timeUs) {
|
||||
return timeUs < 0 ? 0 : C.INDEX_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEventTimeCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEventTime(int index) {
|
||||
Assertions.checkArgument(index == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cue> getCues(long timeUs) {
|
||||
return timeUs >= 0 ? cues : Collections.emptyList();
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package androidx.media3.extractor.text.webvtt;
|
||||
|
||||
import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
|
||||
import static java.lang.Math.min;
|
||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
@ -36,6 +37,7 @@ import android.text.style.TypefaceSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.common.text.HorizontalTextInVerticalContextSpan;
|
||||
import androidx.media3.common.text.RubySpan;
|
||||
@ -230,7 +232,8 @@ public final class WebvttCueParser {
|
||||
}
|
||||
|
||||
/** Create a new {@link Cue} containing {@code text} and with WebVTT default values. */
|
||||
/* package */ static Cue newCueForText(CharSequence text) {
|
||||
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
|
||||
public static Cue newCueForText(CharSequence text) {
|
||||
WebvttCueInfoBuilder infoBuilder = new WebvttCueInfoBuilder();
|
||||
infoBuilder.text = text;
|
||||
return infoBuilder.toCueBuilder().build();
|
||||
|
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.extractor.text.webvtt;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.extractor.text.CuesWithTiming;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.truth.Expect;
|
||||
import java.util.List;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Unit test for {@link Mp4WebvttParser}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class Mp4WebvttParserTest {
|
||||
|
||||
private static final byte[] SINGLE_CUE_SAMPLE = {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1C, // Size
|
||||
0x76,
|
||||
0x74,
|
||||
0x74,
|
||||
0x63, // "vttc" Box type. VTT Cue box begins:
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x14, // Contained payload box's size
|
||||
0x70,
|
||||
0x61,
|
||||
0x79,
|
||||
0x6c, // Contained payload box's type (payl), Cue Payload Box begins:
|
||||
0x48,
|
||||
0x65,
|
||||
0x6c,
|
||||
0x6c,
|
||||
0x6f,
|
||||
0x20,
|
||||
0x57,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x6c,
|
||||
0x64,
|
||||
0x0a // Hello World\n
|
||||
};
|
||||
|
||||
private static final byte[] DOUBLE_CUE_SAMPLE = {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1B, // Size
|
||||
0x76,
|
||||
0x74,
|
||||
0x74,
|
||||
0x63, // "vttc" Box type. First VTT Cue box begins:
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x13, // First contained payload box's size
|
||||
0x70,
|
||||
0x61,
|
||||
0x79,
|
||||
0x6c, // First contained payload box's type (payl), Cue Payload Box begins:
|
||||
0x48,
|
||||
0x65,
|
||||
0x6c,
|
||||
0x6c,
|
||||
0x6f,
|
||||
0x20,
|
||||
0x57,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x6c,
|
||||
0x64, // Hello World
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x17, // Size
|
||||
0x76,
|
||||
0x74,
|
||||
0x74,
|
||||
0x63, // "vttc" Box type. Second VTT Cue box begins:
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0F, // Contained payload box's size
|
||||
0x70,
|
||||
0x61,
|
||||
0x79,
|
||||
0x6c, // Contained payload box's type (payl), Payload begins:
|
||||
0x42,
|
||||
0x79,
|
||||
0x65,
|
||||
0x20,
|
||||
0x42,
|
||||
0x79,
|
||||
0x65 // Bye Bye
|
||||
};
|
||||
|
||||
private static final byte[] NO_CUE_SAMPLE = {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1B, // Size
|
||||
0x74,
|
||||
0x74,
|
||||
0x74,
|
||||
0x63, // "tttc" Box type, which is not a Cue. Should be skipped:
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x13, // Contained payload box's size
|
||||
0x70,
|
||||
0x61,
|
||||
0x79,
|
||||
0x6c, // Contained payload box's type (payl), Cue Payload Box begins:
|
||||
0x48,
|
||||
0x65,
|
||||
0x6c,
|
||||
0x6c,
|
||||
0x6f,
|
||||
0x20,
|
||||
0x57,
|
||||
0x6f,
|
||||
0x72,
|
||||
0x6c,
|
||||
0x64 // Hello World
|
||||
};
|
||||
|
||||
private static final byte[] INCOMPLETE_HEADER_SAMPLE = {
|
||||
0x00, 0x00, 0x00, 0x23, // Size
|
||||
0x76, 0x74, 0x74, 0x63, // "vttc" Box type. VTT Cue box begins:
|
||||
0x00, 0x00, 0x00, 0x14, // Contained payload box's size
|
||||
0x70, 0x61, 0x79, 0x6c, // Contained payload box's type (payl), Cue Payload Box begins:
|
||||
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x0a, // Hello World\n
|
||||
0x00, 0x00, 0x00, 0x07, // Size of an incomplete header, which belongs to the first vttc box.
|
||||
0x76, 0x74, 0x74
|
||||
};
|
||||
|
||||
@Rule public final Expect expect = Expect.create();
|
||||
|
||||
// Positive tests.
|
||||
|
||||
@Test
|
||||
public void singleCueSample() {
|
||||
Mp4WebvttParser parser = new Mp4WebvttParser();
|
||||
List<CuesWithTiming> result = parser.parse(SINGLE_CUE_SAMPLE);
|
||||
// Line feed must be trimmed by the decoder
|
||||
Cue expectedCue = WebvttCueParser.newCueForText("Hello World");
|
||||
assertMp4WebvttSubtitleEquals(result, expectedCue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoCuesSample() {
|
||||
Mp4WebvttParser parser = new Mp4WebvttParser();
|
||||
List<CuesWithTiming> result = parser.parse(DOUBLE_CUE_SAMPLE);
|
||||
Cue firstExpectedCue = WebvttCueParser.newCueForText("Hello World");
|
||||
Cue secondExpectedCue = WebvttCueParser.newCueForText("Bye Bye");
|
||||
assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCueSample() {
|
||||
Mp4WebvttParser parser = new Mp4WebvttParser();
|
||||
List<CuesWithTiming> result = parser.parse(NO_CUE_SAMPLE);
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
// Negative tests.
|
||||
|
||||
@Test
|
||||
public void sampleWithIncompleteHeader() {
|
||||
Mp4WebvttParser parser = new Mp4WebvttParser();
|
||||
assertThrows(IllegalArgumentException.class, () -> parser.parse(INCOMPLETE_HEADER_SAMPLE));
|
||||
}
|
||||
|
||||
// Util methods
|
||||
|
||||
/**
|
||||
* Asserts that the Subtitle's cues (which are all part of the event at t=0) are equal to the
|
||||
* expected Cues.
|
||||
*
|
||||
* @param cuesWithTimings The list of {@link CuesWithTiming} to check.
|
||||
* @param expectedCues The expected {@link Cue}s.
|
||||
*/
|
||||
private void assertMp4WebvttSubtitleEquals(
|
||||
List<CuesWithTiming> cuesWithTimings, Cue... expectedCues) {
|
||||
assertThat(cuesWithTimings).hasSize(1);
|
||||
assertThat(cuesWithTimings.get(0).startTimeUs).isEqualTo(0);
|
||||
ImmutableList<Cue> allCues = cuesWithTimings.get(0).cues;
|
||||
assertThat(allCues).hasSize(expectedCues.length);
|
||||
for (int i = 0; i < allCues.size(); i++) {
|
||||
assertCuesEqual(expectedCues[i], allCues.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/** Asserts that two cues are equal. */
|
||||
private void assertCuesEqual(Cue expected, Cue actual) {
|
||||
expect.withMessage("Cue.line").that(actual.line).isEqualTo(expected.line);
|
||||
expect.withMessage("Cue.lineAnchor").that(actual.lineAnchor).isEqualTo(expected.lineAnchor);
|
||||
expect.withMessage("Cue.lineType").that(actual.lineType).isEqualTo(expected.lineType);
|
||||
expect.withMessage("Cue.position").that(actual.position).isEqualTo(expected.position);
|
||||
expect
|
||||
.withMessage("Cue.positionAnchor")
|
||||
.that(actual.positionAnchor)
|
||||
.isEqualTo(expected.positionAnchor);
|
||||
expect.withMessage("Cue.size").that(actual.size).isEqualTo(expected.size);
|
||||
expect.withMessage("Cue.text").that(actual.text.toString()).isEqualTo(expected.text.toString());
|
||||
expect
|
||||
.withMessage("Cue.textAlignment")
|
||||
.that(actual.textAlignment)
|
||||
.isEqualTo(expected.textAlignment);
|
||||
|
||||
assertThat(expect.hasFailures()).isFalse();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user