Standardise the use of ParsableByteArray
use among SubtitleParsers
PiperOrigin-RevId: 549609028
This commit is contained in:
parent
9128293236
commit
27bda610aa
@ -91,7 +91,6 @@ public final class DvbParser implements SubtitleParser {
|
|||||||
private final DisplayDefinition defaultDisplayDefinition;
|
private final DisplayDefinition defaultDisplayDefinition;
|
||||||
private final ClutDefinition defaultClutDefinition;
|
private final ClutDefinition defaultClutDefinition;
|
||||||
private final SubtitleService subtitleService;
|
private final SubtitleService subtitleService;
|
||||||
private byte[] dataScratch = Util.EMPTY_BYTE_ARRAY;
|
|
||||||
|
|
||||||
private @MonotonicNonNull Bitmap bitmap;
|
private @MonotonicNonNull Bitmap bitmap;
|
||||||
|
|
||||||
@ -131,13 +130,8 @@ public final class DvbParser implements SubtitleParser {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
||||||
// Parse the input data.
|
ParsableBitArray dataBitArray = new ParsableBitArray(data, /* limit= */ offset + length);
|
||||||
if (dataScratch.length < length) {
|
dataBitArray.setPosition(offset);
|
||||||
dataScratch = new byte[length];
|
|
||||||
}
|
|
||||||
System.arraycopy(
|
|
||||||
/* src= */ data, /* scrPos= */ offset, /* dest= */ dataScratch, /* destPos= */ 0, length);
|
|
||||||
ParsableBitArray dataBitArray = new ParsableBitArray(dataScratch, length);
|
|
||||||
while (dataBitArray.bitsLeft() >= 48 // sync_byte (8) + segment header (40)
|
while (dataBitArray.bitsLeft() >= 48 // sync_byte (8) + segment header (40)
|
||||||
&& dataBitArray.readBits(8) == 0x0F) {
|
&& dataBitArray.readBits(8) == 0x0F) {
|
||||||
parseSubtitlingSegment(dataBitArray, subtitleService);
|
parseSubtitlingSegment(dataBitArray, subtitleService);
|
||||||
|
@ -45,7 +45,6 @@ public final class PgsParser implements SubtitleParser {
|
|||||||
private final ParsableByteArray buffer;
|
private final ParsableByteArray buffer;
|
||||||
private final ParsableByteArray inflatedBuffer;
|
private final ParsableByteArray inflatedBuffer;
|
||||||
private final CueBuilder cueBuilder;
|
private final CueBuilder cueBuilder;
|
||||||
private byte[] dataScratch = Util.EMPTY_BYTE_ARRAY;
|
|
||||||
@Nullable private Inflater inflater;
|
@Nullable private Inflater inflater;
|
||||||
|
|
||||||
public PgsParser() {
|
public PgsParser() {
|
||||||
@ -59,16 +58,8 @@ public final class PgsParser implements SubtitleParser {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
||||||
if (offset != 0) {
|
buffer.reset(data, /* limit= */ offset + length);
|
||||||
if (dataScratch.length < length) {
|
buffer.setPosition(offset);
|
||||||
dataScratch = new byte[length];
|
|
||||||
}
|
|
||||||
System.arraycopy(
|
|
||||||
/* src= */ data, /* scrPos= */ offset, /* dest= */ dataScratch, /* destPos= */ 0, length);
|
|
||||||
buffer.reset(dataScratch, length);
|
|
||||||
} else {
|
|
||||||
buffer.reset(data, length);
|
|
||||||
}
|
|
||||||
maybeInflateData(buffer);
|
maybeInflateData(buffer);
|
||||||
cueBuilder.reset();
|
cueBuilder.reset();
|
||||||
ArrayList<Cue> cues = new ArrayList<>();
|
ArrayList<Cue> cues = new ArrayList<>();
|
||||||
|
@ -65,6 +65,7 @@ public final class SsaParser implements SubtitleParser {
|
|||||||
|
|
||||||
private final boolean haveInitializationData;
|
private final boolean haveInitializationData;
|
||||||
@Nullable private final SsaDialogueFormat dialogueFormatFromInitializationData;
|
@Nullable private final SsaDialogueFormat dialogueFormatFromInitializationData;
|
||||||
|
private final ParsableByteArray parsableByteArray;
|
||||||
|
|
||||||
private @MonotonicNonNull Map<String, SsaStyle> styles;
|
private @MonotonicNonNull Map<String, SsaStyle> styles;
|
||||||
|
|
||||||
@ -82,8 +83,6 @@ public final class SsaParser implements SubtitleParser {
|
|||||||
*/
|
*/
|
||||||
private float screenHeight;
|
private float screenHeight;
|
||||||
|
|
||||||
private byte[] dataScratch = Util.EMPTY_BYTE_ARRAY;
|
|
||||||
|
|
||||||
public SsaParser() {
|
public SsaParser() {
|
||||||
this(/* initializationData= */ null);
|
this(/* initializationData= */ null);
|
||||||
}
|
}
|
||||||
@ -100,6 +99,7 @@ public final class SsaParser implements SubtitleParser {
|
|||||||
public SsaParser(@Nullable List<byte[]> initializationData) {
|
public SsaParser(@Nullable List<byte[]> initializationData) {
|
||||||
screenWidth = Cue.DIMEN_UNSET;
|
screenWidth = Cue.DIMEN_UNSET;
|
||||||
screenHeight = Cue.DIMEN_UNSET;
|
screenHeight = Cue.DIMEN_UNSET;
|
||||||
|
parsableByteArray = new ParsableByteArray();
|
||||||
|
|
||||||
if (initializationData != null && !initializationData.isEmpty()) {
|
if (initializationData != null && !initializationData.isEmpty()) {
|
||||||
haveInitializationData = true;
|
haveInitializationData = true;
|
||||||
@ -126,18 +126,14 @@ public final class SsaParser implements SubtitleParser {
|
|||||||
List<List<Cue>> cues = new ArrayList<>();
|
List<List<Cue>> cues = new ArrayList<>();
|
||||||
List<Long> startTimesUs = new ArrayList<>();
|
List<Long> startTimesUs = new ArrayList<>();
|
||||||
|
|
||||||
if (dataScratch.length < length) {
|
parsableByteArray.reset(data, /* limit= */ offset + length);
|
||||||
dataScratch = new byte[length];
|
parsableByteArray.setPosition(offset);
|
||||||
}
|
Charset charset = detectUtfCharset(parsableByteArray);
|
||||||
System.arraycopy(
|
|
||||||
/* src= */ data, /* scrPos= */ offset, /* dest= */ dataScratch, /* destPos= */ 0, length);
|
|
||||||
ParsableByteArray parsableData = new ParsableByteArray(dataScratch, length);
|
|
||||||
Charset charset = detectUtfCharset(parsableData);
|
|
||||||
|
|
||||||
if (!haveInitializationData) {
|
if (!haveInitializationData) {
|
||||||
parseHeader(parsableData, charset);
|
parseHeader(parsableByteArray, charset);
|
||||||
}
|
}
|
||||||
parseEventBody(parsableData, cues, startTimesUs, charset);
|
parseEventBody(parsableByteArray, cues, startTimesUs, charset);
|
||||||
|
|
||||||
ImmutableList.Builder<CuesWithTiming> cuesWithStartTimeAndDuration = ImmutableList.builder();
|
ImmutableList.Builder<CuesWithTiming> cuesWithStartTimeAndDuration = ImmutableList.builder();
|
||||||
for (int i = 0; i < cues.size(); i++) {
|
for (int i = 0; i < cues.size(); i++) {
|
||||||
|
@ -27,7 +27,6 @@ import androidx.media3.common.util.Assertions;
|
|||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
|
||||||
import androidx.media3.extractor.text.CuesWithTiming;
|
import androidx.media3.extractor.text.CuesWithTiming;
|
||||||
import androidx.media3.extractor.text.SubtitleParser;
|
import androidx.media3.extractor.text.SubtitleParser;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
@ -70,11 +69,12 @@ public final class SubripParser implements SubtitleParser {
|
|||||||
|
|
||||||
private final StringBuilder textBuilder;
|
private final StringBuilder textBuilder;
|
||||||
private final ArrayList<String> tags;
|
private final ArrayList<String> tags;
|
||||||
private byte[] dataScratch = Util.EMPTY_BYTE_ARRAY;
|
private final ParsableByteArray parsableByteArray;
|
||||||
|
|
||||||
public SubripParser() {
|
public SubripParser() {
|
||||||
textBuilder = new StringBuilder();
|
textBuilder = new StringBuilder();
|
||||||
tags = new ArrayList<>();
|
tags = new ArrayList<>();
|
||||||
|
parsableByteArray = new ParsableByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -82,16 +82,12 @@ public final class SubripParser implements SubtitleParser {
|
|||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
||||||
ImmutableList.Builder<CuesWithTiming> cues = new ImmutableList.Builder<>();
|
ImmutableList.Builder<CuesWithTiming> cues = new ImmutableList.Builder<>();
|
||||||
|
|
||||||
if (dataScratch.length < length) {
|
parsableByteArray.reset(data, /* limit= */ offset + length);
|
||||||
dataScratch = new byte[length];
|
parsableByteArray.setPosition(offset);
|
||||||
}
|
Charset charset = detectUtfCharset(parsableByteArray);
|
||||||
System.arraycopy(
|
|
||||||
/* src= */ data, /* scrPos= */ offset, /* dest= */ dataScratch, /* destPos= */ 0, length);
|
|
||||||
ParsableByteArray subripData = new ParsableByteArray(dataScratch, length);
|
|
||||||
Charset charset = detectUtfCharset(subripData);
|
|
||||||
|
|
||||||
@Nullable String currentLine;
|
@Nullable String currentLine;
|
||||||
while ((currentLine = subripData.readLine(charset)) != null) {
|
while ((currentLine = parsableByteArray.readLine(charset)) != null) {
|
||||||
if (currentLine.length() == 0) {
|
if (currentLine.length() == 0) {
|
||||||
// Skip blank lines.
|
// Skip blank lines.
|
||||||
continue;
|
continue;
|
||||||
@ -106,7 +102,7 @@ public final class SubripParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read and parse the timing line.
|
// Read and parse the timing line.
|
||||||
currentLine = subripData.readLine(charset);
|
currentLine = parsableByteArray.readLine(charset);
|
||||||
if (currentLine == null) {
|
if (currentLine == null) {
|
||||||
Log.w(TAG, "Unexpected end");
|
Log.w(TAG, "Unexpected end");
|
||||||
break;
|
break;
|
||||||
@ -126,13 +122,13 @@ public final class SubripParser implements SubtitleParser {
|
|||||||
// Read and parse the text and tags.
|
// Read and parse the text and tags.
|
||||||
textBuilder.setLength(0);
|
textBuilder.setLength(0);
|
||||||
tags.clear();
|
tags.clear();
|
||||||
currentLine = subripData.readLine(charset);
|
currentLine = parsableByteArray.readLine(charset);
|
||||||
while (!TextUtils.isEmpty(currentLine)) {
|
while (!TextUtils.isEmpty(currentLine)) {
|
||||||
if (textBuilder.length() > 0) {
|
if (textBuilder.length() > 0) {
|
||||||
textBuilder.append("<br>");
|
textBuilder.append("<br>");
|
||||||
}
|
}
|
||||||
textBuilder.append(processLine(currentLine, tags));
|
textBuilder.append(processLine(currentLine, tags));
|
||||||
currentLine = subripData.readLine(charset);
|
currentLine = parsableByteArray.readLine(charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
Spanned text = Html.fromHtml(textBuilder.toString());
|
Spanned text = Html.fromHtml(textBuilder.toString());
|
||||||
|
@ -79,7 +79,6 @@ public final class Tx3gParser implements SubtitleParser {
|
|||||||
private final String defaultFontFamily;
|
private final String defaultFontFamily;
|
||||||
private final float defaultVerticalPlacement;
|
private final float defaultVerticalPlacement;
|
||||||
private final int calculatedVideoTrackHeight;
|
private final int calculatedVideoTrackHeight;
|
||||||
private byte[] dataScratch = Util.EMPTY_BYTE_ARRAY;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up a new {@link Tx3gParser} with default values.
|
* Sets up a new {@link Tx3gParser} with default values.
|
||||||
@ -128,16 +127,8 @@ public final class Tx3gParser implements SubtitleParser {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
||||||
if (offset != 0) {
|
parsableByteArray.reset(data, /* limit= */ offset + length);
|
||||||
if (dataScratch.length < length) {
|
parsableByteArray.setPosition(offset);
|
||||||
dataScratch = new byte[length];
|
|
||||||
}
|
|
||||||
System.arraycopy(
|
|
||||||
/* src= */ data, /* scrPos= */ offset, /* dest= */ dataScratch, /* destPos= */ 0, length);
|
|
||||||
parsableByteArray.reset(dataScratch, length);
|
|
||||||
} else {
|
|
||||||
parsableByteArray.reset(data, length);
|
|
||||||
}
|
|
||||||
String cueTextString = readSubtitleText(parsableByteArray);
|
String cueTextString = readSubtitleText(parsableByteArray);
|
||||||
if (cueTextString.isEmpty()) {
|
if (cueTextString.isEmpty()) {
|
||||||
return ImmutableList.of(
|
return ImmutableList.of(
|
||||||
|
@ -46,39 +46,30 @@ public final class Mp4WebvttParser implements SubtitleParser {
|
|||||||
@SuppressWarnings("ConstantCaseForConstants")
|
@SuppressWarnings("ConstantCaseForConstants")
|
||||||
private static final int TYPE_vttc = 0x76747463;
|
private static final int TYPE_vttc = 0x76747463;
|
||||||
|
|
||||||
private final ParsableByteArray sampleData;
|
private final ParsableByteArray parsableByteArray;
|
||||||
private byte[] dataScratch = Util.EMPTY_BYTE_ARRAY;
|
|
||||||
|
|
||||||
public Mp4WebvttParser() {
|
public Mp4WebvttParser() {
|
||||||
sampleData = new ParsableByteArray();
|
parsableByteArray = new ParsableByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
||||||
if (offset != 0) {
|
parsableByteArray.reset(data, /* limit= */ offset + length);
|
||||||
if (dataScratch.length < length) {
|
parsableByteArray.setPosition(offset);
|
||||||
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<>();
|
List<Cue> cues = new ArrayList<>();
|
||||||
while (sampleData.bytesLeft() > 0) {
|
while (parsableByteArray.bytesLeft() > 0) {
|
||||||
// Webvtt in Mp4 samples have boxes inside of them, so we have to do a traditional box
|
// 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.
|
// parsing: first 4 bytes size and then 4 bytes type.
|
||||||
checkArgument(
|
checkArgument(
|
||||||
sampleData.bytesLeft() >= BOX_HEADER_SIZE,
|
parsableByteArray.bytesLeft() >= BOX_HEADER_SIZE,
|
||||||
"Incomplete Mp4Webvtt Top Level box header found.");
|
"Incomplete Mp4Webvtt Top Level box header found.");
|
||||||
int boxSize = sampleData.readInt();
|
int boxSize = parsableByteArray.readInt();
|
||||||
int boxType = sampleData.readInt();
|
int boxType = parsableByteArray.readInt();
|
||||||
if (boxType == TYPE_vttc) {
|
if (boxType == TYPE_vttc) {
|
||||||
cues.add(parseVttCueBox(sampleData, boxSize - BOX_HEADER_SIZE));
|
cues.add(parseVttCueBox(parsableByteArray, boxSize - BOX_HEADER_SIZE));
|
||||||
} else {
|
} else {
|
||||||
// Peers of the VTTCueBox are still not supported and are skipped.
|
// Peers of the VTTCueBox are still not supported and are skipped.
|
||||||
sampleData.skipBytes(boxSize - BOX_HEADER_SIZE);
|
parsableByteArray.skipBytes(boxSize - BOX_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cues.isEmpty()
|
return cues.isEmpty()
|
||||||
|
@ -53,7 +53,7 @@ public final class WebvttParser implements SubtitleParser {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
||||||
parsableWebvttData.reset(data, length);
|
parsableWebvttData.reset(data, /* limit= */ offset + length);
|
||||||
parsableWebvttData.setPosition(offset);
|
parsableWebvttData.setPosition(offset);
|
||||||
List<WebvttCssStyle> definedStyles = new ArrayList<>();
|
List<WebvttCssStyle> definedStyles = new ArrayList<>();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user