mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add some nullness annotations, re-jig some logic and reformat
This commit is contained in:
parent
391b72e257
commit
8520c66fd8
@ -212,7 +212,7 @@ public final class Util {
|
|||||||
private static final String ISM_HLS_FORMAT_EXTENSION = "format=m3u8-aapl";
|
private static final String ISM_HLS_FORMAT_EXTENSION = "format=m3u8-aapl";
|
||||||
private static final String ISM_DASH_FORMAT_EXTENSION = "format=mpd-time-csf";
|
private static final String ISM_DASH_FORMAT_EXTENSION = "format=mpd-time-csf";
|
||||||
|
|
||||||
private static final int INFLATE_HEADER = 0x78;
|
private static final int ZLIB_INFLATE_HEADER = 0x78;
|
||||||
|
|
||||||
// Replacement map of ISO language codes used for normalization.
|
// Replacement map of ISO language codes used for normalization.
|
||||||
@Nullable private static HashMap<String, String> languageTagReplacementMap;
|
@Nullable private static HashMap<String, String> languageTagReplacementMap;
|
||||||
@ -3105,25 +3105,23 @@ public final class Util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uncompresses the data in {@code input} if it starts with {@code INFLATE_HEADER}
|
* Uncompresses the data in {@code input} if it starts with the zlib marker {@code 0x78}.
|
||||||
* ({@code 0x78}).
|
|
||||||
*
|
*
|
||||||
* @param input Wraps the compressed input data.
|
* @param input Wraps the compressed input data.
|
||||||
* @param output Wraps an output buffer to be used to store the uncompressed data. If {@code
|
* @param output Wraps an output buffer to be used to store the uncompressed data. If {@code
|
||||||
* output.data} isn't big enough to hold the uncompressed data, a new array is created. If
|
* output.data} isn't big enough to hold the uncompressed data, a new array is created. If
|
||||||
* {@code true} is returned then the output's position will be set to 0 and its limit will be
|
* {@code true} is returned then the output's position will be set to 0 and its limit will be
|
||||||
* set to the length of the uncompressed data.
|
* set to the length of the uncompressed data.
|
||||||
* @param inflater If not null, used to uncompressed the input. Otherwise a new {@link Inflater}
|
* @param inflater If not null, used to uncompress the input. Otherwise a new {@link Inflater} is
|
||||||
* is created.
|
* created.
|
||||||
* @return Whether the input is uncompressed successfully.
|
* @return Whether the input is uncompressed successfully.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public static boolean maybeInflate(
|
public static boolean maybeInflate(
|
||||||
ParsableByteArray input, ParsableByteArray output, @Nullable Inflater inflater) {
|
ParsableByteArray input, ParsableByteArray output, @Nullable Inflater inflater) {
|
||||||
if (input.bytesLeft() > 0 && input.peekUnsignedByte() == INFLATE_HEADER) {
|
return input.bytesLeft() > 0
|
||||||
return inflate(input, output, inflater);
|
&& input.peekUnsignedByte() == ZLIB_INFLATE_HEADER
|
||||||
}
|
&& inflate(input, output, inflater);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,11 +22,11 @@ import androidx.media3.common.MimeTypes;
|
|||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.extractor.text.dvb.DvbParser;
|
import androidx.media3.extractor.text.dvb.DvbParser;
|
||||||
import androidx.media3.extractor.text.pgs.PgsParser;
|
import androidx.media3.extractor.text.pgs.PgsParser;
|
||||||
import androidx.media3.extractor.text.vobsub.VobsubParser;
|
|
||||||
import androidx.media3.extractor.text.ssa.SsaParser;
|
import androidx.media3.extractor.text.ssa.SsaParser;
|
||||||
import androidx.media3.extractor.text.subrip.SubripParser;
|
import androidx.media3.extractor.text.subrip.SubripParser;
|
||||||
import androidx.media3.extractor.text.ttml.TtmlParser;
|
import androidx.media3.extractor.text.ttml.TtmlParser;
|
||||||
import androidx.media3.extractor.text.tx3g.Tx3gParser;
|
import androidx.media3.extractor.text.tx3g.Tx3gParser;
|
||||||
|
import androidx.media3.extractor.text.vobsub.VobsubParser;
|
||||||
import androidx.media3.extractor.text.webvtt.Mp4WebvttParser;
|
import androidx.media3.extractor.text.webvtt.Mp4WebvttParser;
|
||||||
import androidx.media3.extractor.text.webvtt.WebvttParser;
|
import androidx.media3.extractor.text.webvtt.WebvttParser;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package androidx.media3.extractor.text.vobsub;
|
package androidx.media3.extractor.text.vobsub;
|
||||||
|
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Rect;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
@ -16,12 +18,10 @@ 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.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.Inflater;
|
import java.util.zip.Inflater;
|
||||||
import android.graphics.Rect;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
// Much of this is taken from or very similar to PgsParser
|
// Much of this is taken from or very similar to PgsParser
|
||||||
|
|
||||||
@ -36,19 +36,18 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
public static final @CueReplacementBehavior int CUE_REPLACEMENT_BEHAVIOR =
|
public static final @CueReplacementBehavior int CUE_REPLACEMENT_BEHAVIOR =
|
||||||
Format.CUE_REPLACEMENT_BEHAVIOR_REPLACE;
|
Format.CUE_REPLACEMENT_BEHAVIOR_REPLACE;
|
||||||
|
|
||||||
private static final int DEFAULT_DURATION = 5000000;
|
private static final int DEFAULT_DURATION_US = 5_000_000;
|
||||||
|
|
||||||
private final ParsableByteArray buffer;
|
private final ParsableByteArray scratch;
|
||||||
private final ParsableByteArray inflatedBuffer;
|
private final ParsableByteArray inflatedScratch;
|
||||||
private final CueBuilder cueBuilder;
|
private final CueBuilder cueBuilder;
|
||||||
@Nullable private Inflater inflater;
|
@Nullable private Inflater inflater;
|
||||||
|
|
||||||
public VobsubParser(List<byte[]> initializationData) {
|
public VobsubParser(List<byte[]> initializationData) {
|
||||||
|
scratch = new ParsableByteArray();
|
||||||
buffer = new ParsableByteArray();
|
inflatedScratch = new ParsableByteArray();
|
||||||
inflatedBuffer = new ParsableByteArray();
|
|
||||||
cueBuilder = new CueBuilder();
|
cueBuilder = new CueBuilder();
|
||||||
cueBuilder.parseIdx(new String(initializationData.get(0), StandardCharsets.UTF_8));
|
cueBuilder.parseIdx(new String(initializationData.get(0), UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -63,32 +62,33 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
int length,
|
int length,
|
||||||
OutputOptions outputOptions,
|
OutputOptions outputOptions,
|
||||||
Consumer<CuesWithTiming> output) {
|
Consumer<CuesWithTiming> output) {
|
||||||
|
scratch.reset(data, offset + length);
|
||||||
buffer.reset(data, offset + length);
|
scratch.setPosition(offset);
|
||||||
buffer.setPosition(offset);
|
@Nullable Cue cue = parse();
|
||||||
if (inflater == null) {
|
|
||||||
inflater = new Inflater();
|
|
||||||
}
|
|
||||||
if (Util.maybeInflate(buffer, inflatedBuffer, inflater)) {
|
|
||||||
buffer.reset(inflatedBuffer.getData(), inflatedBuffer.limit());
|
|
||||||
}
|
|
||||||
cueBuilder.reset();
|
|
||||||
Cue cue = null;
|
|
||||||
|
|
||||||
int blen = buffer.bytesLeft();
|
|
||||||
if (blen >= 2) {
|
|
||||||
int len = buffer.readUnsignedShort();
|
|
||||||
|
|
||||||
if (len == blen) {
|
|
||||||
cueBuilder.parseSpu(buffer);
|
|
||||||
cue = cueBuilder.build(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output.accept(
|
output.accept(
|
||||||
new CuesWithTiming(
|
new CuesWithTiming(
|
||||||
cue != null ? ImmutableList.of(cue) : ImmutableList.of(),
|
cue != null ? ImmutableList.of(cue) : ImmutableList.of(),
|
||||||
/* startTimeUs= */ C.TIME_UNSET,
|
/* startTimeUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ DEFAULT_DURATION));
|
/* durationUs= */ DEFAULT_DURATION_US));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Cue parse() {
|
||||||
|
if (inflater == null) {
|
||||||
|
inflater = new Inflater();
|
||||||
|
}
|
||||||
|
if (Util.maybeInflate(scratch, inflatedScratch, inflater)) {
|
||||||
|
scratch.reset(inflatedScratch.getData(), inflatedScratch.limit());
|
||||||
|
}
|
||||||
|
cueBuilder.reset();
|
||||||
|
Cue cue = null;
|
||||||
|
|
||||||
|
int bytesLeft = scratch.bytesLeft();
|
||||||
|
if (bytesLeft < 2 || scratch.readUnsignedShort() != bytesLeft) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
cueBuilder.parseSpu(scratch);
|
||||||
|
return cueBuilder.build(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class CueBuilder {
|
private static final class CueBuilder {
|
||||||
@ -99,16 +99,17 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
private static final int CMD_OFFSETS = 6;
|
private static final int CMD_OFFSETS = 6;
|
||||||
private static final int CMD_END = 255;
|
private static final int CMD_END = 255;
|
||||||
|
|
||||||
|
private final int[] colors;
|
||||||
|
|
||||||
private boolean hasPlane;
|
private boolean hasPlane;
|
||||||
private boolean hasColors;
|
private boolean hasColors;
|
||||||
private boolean hasDataOffsets;
|
private boolean hasDataOffsets;
|
||||||
private int[] palette;
|
private int @MonotonicNonNull [] palette;
|
||||||
private int planeWidth;
|
private int planeWidth;
|
||||||
private int planeHeight;
|
private int planeHeight;
|
||||||
private int[] colors;
|
private @Nullable Rect boundingBox;
|
||||||
private Rect boundingBox;
|
private int dataOffset0;
|
||||||
private int dataOffset0, dataOffset1;
|
private int dataOffset1;
|
||||||
private int dataSize;
|
|
||||||
|
|
||||||
public CueBuilder() {
|
public CueBuilder() {
|
||||||
colors = new int[4];
|
colors = new int[4];
|
||||||
@ -124,10 +125,8 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
palette[i] = parseColor(values[i].trim());
|
palette[i] = parseColor(values[i].trim());
|
||||||
}
|
}
|
||||||
} else if (line.startsWith("size: ")) {
|
} else if (line.startsWith("size: ")) {
|
||||||
|
// We need this line to calculate the relative positions and size required when building
|
||||||
// NOTE: we need this line to calculate the relative positions
|
// the Cue below.
|
||||||
// and size required by Cue.Builder() below.
|
|
||||||
|
|
||||||
String[] sizes = Util.split(line.substring("size: ".length()).trim(), "x");
|
String[] sizes = Util.split(line.substring("size: ".length()).trim(), "x");
|
||||||
|
|
||||||
if (sizes.length == 2) {
|
if (sizes.length == 2) {
|
||||||
@ -151,42 +150,38 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void parseSpu(ParsableByteArray buffer) {
|
public void parseSpu(ParsableByteArray buffer) {
|
||||||
|
if (palette == null || !hasPlane) {
|
||||||
// Give up if we don't have the color palette or the video size.
|
// Give up if we don't have the color palette or the video size.
|
||||||
// (See also the NOTE above)
|
return;
|
||||||
|
}
|
||||||
if (palette == null || !hasPlane) return;
|
buffer.skipBytes(buffer.readUnsignedShort());
|
||||||
|
|
||||||
int pos = buffer.getPosition();
|
|
||||||
|
|
||||||
dataSize = buffer.readUnsignedShort();
|
|
||||||
pos += dataSize;
|
|
||||||
buffer.setPosition(pos);
|
|
||||||
|
|
||||||
int end = buffer.readUnsignedShort();
|
int end = buffer.readUnsignedShort();
|
||||||
parseControl(buffer, end);
|
parseControl(buffer, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseControl(ParsableByteArray buffer, int end) {
|
private void parseControl(ParsableByteArray buffer, int end) {
|
||||||
|
|
||||||
while (buffer.getPosition() < end && buffer.bytesLeft() > 0) {
|
while (buffer.getPosition() < end && buffer.bytesLeft() > 0) {
|
||||||
switch (buffer.readUnsignedByte()) {
|
switch (buffer.readUnsignedByte()) {
|
||||||
case CMD_COLORS:
|
case CMD_COLORS:
|
||||||
if (!parseControlColors(buffer)) return;
|
if (!parseControlColors(buffer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_ALPHA:
|
case CMD_ALPHA:
|
||||||
if (!parseControlAlpha(buffer)) return;
|
if (!parseControlAlpha(buffer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_AREA:
|
case CMD_AREA:
|
||||||
if (!parseControlArea(buffer)) return;
|
if (!parseControlArea(buffer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_OFFSETS:
|
case CMD_OFFSETS:
|
||||||
if (!parseControlOffsets(buffer)) return;
|
if (!parseControlOffsets(buffer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CMD_END:
|
case CMD_END:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -194,7 +189,9 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean parseControlColors(ParsableByteArray buffer) {
|
private boolean parseControlColors(ParsableByteArray buffer) {
|
||||||
if (buffer.bytesLeft() < 2) return false;
|
if (buffer.bytesLeft() < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int byte0 = buffer.readUnsignedByte();
|
int byte0 = buffer.readUnsignedByte();
|
||||||
int byte1 = buffer.readUnsignedByte();
|
int byte1 = buffer.readUnsignedByte();
|
||||||
@ -209,7 +206,9 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean parseControlAlpha(ParsableByteArray buffer) {
|
private boolean parseControlAlpha(ParsableByteArray buffer) {
|
||||||
if (buffer.bytesLeft() < 2) return false;
|
if (buffer.bytesLeft() < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int byte0 = buffer.readUnsignedByte();
|
int byte0 = buffer.readUnsignedByte();
|
||||||
int byte1 = buffer.readUnsignedByte();
|
int byte1 = buffer.readUnsignedByte();
|
||||||
@ -223,7 +222,9 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean parseControlArea(ParsableByteArray buffer) {
|
private boolean parseControlArea(ParsableByteArray buffer) {
|
||||||
if (buffer.bytesLeft() < 6) return false;
|
if (buffer.bytesLeft() < 6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int byte0 = buffer.readUnsignedByte();
|
int byte0 = buffer.readUnsignedByte();
|
||||||
int byte1 = buffer.readUnsignedByte();
|
int byte1 = buffer.readUnsignedByte();
|
||||||
@ -245,7 +246,9 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean parseControlOffsets(ParsableByteArray buffer) {
|
private boolean parseControlOffsets(ParsableByteArray buffer) {
|
||||||
if (buffer.bytesLeft() < 4) return false;
|
if (buffer.bytesLeft() < 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
dataOffset0 = buffer.readUnsignedShort();
|
dataOffset0 = buffer.readUnsignedShort();
|
||||||
dataOffset1 = buffer.readUnsignedShort();
|
dataOffset1 = buffer.readUnsignedShort();
|
||||||
@ -255,8 +258,7 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getColor(int index) {
|
private int getColor(int index) {
|
||||||
if (index >= 0 && index < palette.length) return palette[index];
|
return index >= 0 && index < palette.length ? palette[index] : palette[0];
|
||||||
return palette[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int setAlpha(int color, int alpha) {
|
private int setAlpha(int color, int alpha) {
|
||||||
@ -283,7 +285,9 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
bitBuffer.reset(buffer);
|
bitBuffer.reset(buffer);
|
||||||
parseRleData(bitBuffer, 1, bitmapData);
|
parseRleData(bitBuffer, 1, bitmapData);
|
||||||
|
|
||||||
Bitmap bitmap = Bitmap.createBitmap(bitmapData, boundingBox.width(), boundingBox.height(), Bitmap.Config.ARGB_8888);
|
Bitmap bitmap =
|
||||||
|
Bitmap.createBitmap(
|
||||||
|
bitmapData, boundingBox.width(), boundingBox.height(), Bitmap.Config.ARGB_8888);
|
||||||
|
|
||||||
return new Cue.Builder()
|
return new Cue.Builder()
|
||||||
.setBitmap(bitmap)
|
.setBitmap(bitmap)
|
||||||
@ -297,9 +301,9 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse run-length encoded data into the {@code bitmapData} array. The
|
* Parse run-length encoded data into the {@code bitmapData} array. The subtitle bitmap is
|
||||||
* subtitle bitmap is encoded in two blocks of interlaced lines, {@code y}
|
* encoded in two blocks of interlaced lines, {@code y} gives the index of the starting line (0
|
||||||
* gives the index of the starting line (0 or 1).
|
* or 1).
|
||||||
*
|
*
|
||||||
* @param bitBuffer The RLE encoded data.
|
* @param bitBuffer The RLE encoded data.
|
||||||
* @param y Index of the first line.
|
* @param y Index of the first line.
|
||||||
@ -316,7 +320,6 @@ public final class VobsubParser implements SubtitleParser {
|
|||||||
parseRun(bitBuffer, run);
|
parseRun(bitBuffer, run);
|
||||||
|
|
||||||
int length = min(run.length, width - x);
|
int length = min(run.length, width - x);
|
||||||
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
Arrays.fill(bitmapData, outIndex, outIndex + length, run.color);
|
Arrays.fill(bitmapData, outIndex, outIndex + length, run.color);
|
||||||
outIndex += length;
|
outIndex += length;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user